Home Home > GIT Browse > vanilla
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKernel Build Daemon <kbuild@suse.de>2019-07-15 12:03:18 +0200
committerKernel Build Daemon <kbuild@suse.de>2019-07-15 12:03:18 +0200
commit87886ea3442bf6bcca99ff536abbdd2d8c2fd7ad (patch)
treed4c6880d50834ca972665616f9b74a8bc20ef8ed
parent1f9ef55feca87a8439cfeb5a9aa8c000aa7565bd (diff)
Automatically updated to 5.2-8441-gfec88ab0af97
-rw-r--r--Documentation/ABI/stable/sysfs-driver-mlxreg-io20
-rw-r--r--Documentation/ABI/testing/sysfs-platform-asus-wmi10
-rw-r--r--Documentation/devicetree/bindings/mailbox/omap-mailbox.txt59
-rw-r--r--Documentation/devicetree/bindings/misc/olpc,xo1.75-ec.txt23
-rw-r--r--Documentation/ioctl/ioctl-number.txt1
-rw-r--r--Documentation/vm/hmm.rst166
-rw-r--r--MAINTAINERS8
-rw-r--r--arch/powerpc/mm/mem.c10
-rw-r--r--arch/um/Kconfig12
-rw-r--r--arch/um/configs/i386_defconfig1
-rw-r--r--arch/um/configs/x86_64_defconfig1
-rw-r--r--arch/um/drivers/chan_kern.c52
-rw-r--r--arch/um/drivers/ssl.c1
-rw-r--r--arch/um/drivers/ssl.h13
-rw-r--r--arch/um/include/asm/mmu_context.h2
-rw-r--r--arch/um/include/shared/os.h10
-rw-r--r--arch/um/include/shared/timer-internal.h48
-rw-r--r--arch/um/kernel/irq.c9
-rw-r--r--arch/um/kernel/process.c42
-rw-r--r--arch/um/kernel/skas/Makefile2
-rw-r--r--arch/um/kernel/skas/syscall.c11
-rw-r--r--arch/um/kernel/time.c131
-rw-r--r--arch/um/os-Linux/time.c127
-rw-r--r--arch/x86/Kconfig1
-rw-r--r--arch/x86/entry/vdso/Makefile12
-rw-r--r--arch/x86/events/amd/uncore.c15
-rw-r--r--arch/x86/events/intel/core.c8
-rw-r--r--arch/x86/include/asm/olpc.h31
-rw-r--r--arch/x86/mm/init_64.c8
-rw-r--r--arch/x86/platform/olpc/olpc.c119
-rw-r--r--arch/x86/platform/olpc/olpc_dt.c2
-rw-r--r--drivers/dax/dax-private.h4
-rw-r--r--drivers/dax/device.c41
-rw-r--r--drivers/dax/pmem/core.c2
-rw-r--r--drivers/gpu/drm/nouveau/Kconfig6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dmem.c103
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_svm.c2
-rw-r--r--drivers/hid/hid-asus.c2
-rw-r--r--drivers/hid/hid-sensor-custom.c2
-rw-r--r--drivers/input/mousedev.c2
-rw-r--r--drivers/mailbox/Kconfig2
-rw-r--r--drivers/mailbox/arm_mhu.c11
-rw-r--r--drivers/mailbox/bcm-flexrm-mailbox.c6
-rw-r--r--drivers/mailbox/imx-mailbox.c4
-rw-r--r--drivers/mailbox/mailbox.c6
-rw-r--r--drivers/mailbox/omap-mailbox.c43
-rw-r--r--drivers/mailbox/stm32-ipcc.c37
-rw-r--r--drivers/mailbox/tegra-hsp.c20
-rw-r--r--drivers/md/md.c3
-rw-r--r--drivers/nvdimm/pfn_devs.c3
-rw-r--r--drivers/nvdimm/pmem.c51
-rw-r--r--drivers/pci/p2pdma.c52
-rw-r--r--drivers/platform/Kconfig2
-rw-r--r--drivers/platform/Makefile2
-rw-r--r--drivers/platform/olpc/Kconfig29
-rw-r--r--drivers/platform/olpc/Makefile3
-rw-r--r--drivers/platform/olpc/olpc-ec.c174
-rw-r--r--drivers/platform/olpc/olpc-xo175-ec.c753
-rw-r--r--drivers/platform/x86/Kconfig16
-rw-r--r--drivers/platform/x86/Makefile4
-rw-r--r--drivers/platform/x86/acer-wmi.c33
-rw-r--r--drivers/platform/x86/asus-nb-wmi.c3
-rw-r--r--drivers/platform/x86/asus-wmi.c477
-rw-r--r--drivers/platform/x86/dell-laptop.c5
-rw-r--r--drivers/platform/x86/dell-smbios-wmi.c2
-rw-r--r--drivers/platform/x86/dell-wmi-descriptor.c3
-rw-r--r--drivers/platform/x86/dell-wmi.c2
-rw-r--r--drivers/platform/x86/hp_accel.c1
-rw-r--r--drivers/platform/x86/huawei-wmi.c2
-rw-r--r--drivers/platform/x86/ideapad-laptop.c36
-rw-r--r--drivers/platform/x86/intel-wmi-thunderbolt.c3
-rw-r--r--drivers/platform/x86/intel_int0002_vgpio.c22
-rw-r--r--drivers/platform/x86/intel_menlow.c8
-rw-r--r--drivers/platform/x86/intel_pmc_core.c63
-rw-r--r--drivers/platform/x86/intel_pmc_core_pltdrv.c62
-rw-r--r--drivers/platform/x86/intel_speed_select_if/Kconfig17
-rw-r--r--drivers/platform/x86/intel_speed_select_if/Makefile10
-rw-r--r--drivers/platform/x86/intel_speed_select_if/isst_if_common.c672
-rw-r--r--drivers/platform/x86/intel_speed_select_if/isst_if_common.h69
-rw-r--r--drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c216
-rw-r--r--drivers/platform/x86/intel_speed_select_if/isst_if_mbox_pci.c214
-rw-r--r--drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c180
-rw-r--r--drivers/platform/x86/intel_telemetry_debugfs.c78
-rw-r--r--drivers/platform/x86/mlx-platform.c216
-rw-r--r--drivers/platform/x86/pcengines-apuv2.c4
-rw-r--r--drivers/platform/x86/pmc_atom.c51
-rw-r--r--drivers/platform/x86/samsung-laptop.c89
-rw-r--r--drivers/platform/x86/touchscreen_dmi.c28
-rw-r--r--drivers/platform/x86/wmi-bmof.c2
-rw-r--r--drivers/platform/x86/wmi.c44
-rw-r--r--drivers/platform/x86/xiaomi-wmi.c92
-rw-r--r--drivers/power/supply/Kconfig2
-rw-r--r--drivers/power/supply/olpc_battery.c1
-rw-r--r--fs/ecryptfs/crypto.c42
-rw-r--r--fs/ecryptfs/debug.c22
-rw-r--r--fs/ecryptfs/inode.c2
-rw-r--r--fs/ecryptfs/keystore.c9
-rw-r--r--fs/io_uring.c3
-rw-r--r--fs/proc/task_mmu.c2
-rw-r--r--fs/ubifs/Kconfig13
-rw-r--r--fs/ubifs/auth.c86
-rw-r--r--fs/ubifs/compress.c27
-rw-r--r--fs/ubifs/log.c5
-rw-r--r--fs/ubifs/master.c53
-rw-r--r--fs/ubifs/orphan.c94
-rw-r--r--fs/ubifs/recovery.c2
-rw-r--r--fs/ubifs/sb.c52
-rw-r--r--fs/ubifs/super.c46
-rw-r--r--fs/ubifs/tnc.c16
-rw-r--r--fs/ubifs/ubifs-media.h30
-rw-r--r--fs/ubifs/ubifs.h6
-rw-r--r--include/linux/acpi.h1
-rw-r--r--include/linux/hmm.h302
-rw-r--r--include/linux/ioport.h5
-rw-r--r--include/linux/memremap.h75
-rw-r--r--include/linux/mm.h28
-rw-r--r--include/linux/mm_types.h4
-rw-r--r--include/linux/mod_devicetable.h1
-rw-r--r--include/linux/olpc-ec.h37
-rw-r--r--include/linux/omap-mailbox.h4
-rw-r--r--include/linux/percpu-refcount.h10
-rw-r--r--include/linux/perf_event.h5
-rw-r--r--include/linux/platform_data/x86/asus-wmi.h5
-rw-r--r--include/linux/swapops.h15
-rw-r--r--include/linux/wmi.h2
-rw-r--r--include/uapi/linux/isst_if.h172
-rw-r--r--kernel/events/core.c83
-rw-r--r--kernel/fork.c1
-rw-r--r--kernel/locking/lockdep_proc.c5
-rw-r--r--kernel/memremap.c194
-rw-r--r--kernel/resource.c39
-rw-r--r--kernel/sched/core.c4
-rw-r--r--lib/percpu-refcount.c13
-rw-r--r--mm/Kconfig50
-rw-r--r--mm/Makefile2
-rw-r--r--mm/gup.c7
-rw-r--r--mm/hmm.c587
-rw-r--r--mm/madvise.c2
-rw-r--r--mm/memcontrol.c13
-rw-r--r--mm/memory-failure.c6
-rw-r--r--mm/memory.c49
-rw-r--r--mm/memory_hotplug.c6
-rw-r--r--mm/mempolicy.c1
-rw-r--r--mm/migrate.c28
-rw-r--r--mm/page_alloc.c13
-rw-r--r--mm/swap.c13
-rw-r--r--scripts/coccinelle/api/stream_open.cocci9
-rw-r--r--tools/Makefile12
-rw-r--r--tools/include/linux/zalloc.h12
-rw-r--r--tools/lib/zalloc.c15
-rw-r--r--tools/perf/MANIFEST1
-rw-r--r--tools/perf/arch/arm/annotate/instructions.c1
-rw-r--r--tools/perf/arch/arm/util/auxtrace.c1
-rw-r--r--tools/perf/arch/arm/util/cs-etm.c1
-rw-r--r--tools/perf/arch/arm64/util/arm-spe.c1
-rw-r--r--tools/perf/arch/common.c3
-rw-r--r--tools/perf/arch/powerpc/util/perf_regs.c4
-rw-r--r--tools/perf/arch/s390/util/auxtrace.c1
-rw-r--r--tools/perf/arch/s390/util/header.c3
-rw-r--r--tools/perf/arch/x86/util/event.c2
-rw-r--r--tools/perf/arch/x86/util/intel-bts.c2
-rw-r--r--tools/perf/arch/x86/util/intel-pt.c2
-rw-r--r--tools/perf/arch/x86/util/perf_regs.c2
-rw-r--r--tools/perf/bench/futex-hash.c3
-rw-r--r--tools/perf/bench/futex-lock-pi.c3
-rw-r--r--tools/perf/bench/mem-functions.c2
-rw-r--r--tools/perf/bench/numa.c2
-rw-r--r--tools/perf/builtin-annotate.c2
-rw-r--r--tools/perf/builtin-bench.c2
-rw-r--r--tools/perf/builtin-c2c.c2
-rw-r--r--tools/perf/builtin-config.c1
-rw-r--r--tools/perf/builtin-diff.c2
-rw-r--r--tools/perf/builtin-ftrace.c2
-rw-r--r--tools/perf/builtin-help.c2
-rw-r--r--tools/perf/builtin-inject.c2
-rw-r--r--tools/perf/builtin-kmem.c2
-rw-r--r--tools/perf/builtin-kvm.c2
-rw-r--r--tools/perf/builtin-lock.c10
-rw-r--r--tools/perf/builtin-probe.c2
-rw-r--r--tools/perf/builtin-record.c4
-rw-r--r--tools/perf/builtin-report.c4
-rw-r--r--tools/perf/builtin-sched.c2
-rw-r--r--tools/perf/builtin-script.c5
-rw-r--r--tools/perf/builtin-stat.c8
-rw-r--r--tools/perf/builtin-timechart.c4
-rw-r--r--tools/perf/builtin-top.c8
-rw-r--r--tools/perf/builtin-trace.c7
-rw-r--r--tools/perf/perf.c2
-rw-r--r--tools/perf/pmu-events/jevents.c2
-rw-r--r--tools/perf/scripts/python/export-to-postgresql.py2
-rw-r--r--tools/perf/scripts/python/export-to-sqlite.py2
-rw-r--r--tools/perf/tests/dwarf-unwind.c5
-rw-r--r--tools/perf/tests/expr.c3
-rw-r--r--tools/perf/tests/llvm.c1
-rw-r--r--tools/perf/tests/mem2node.c3
-rw-r--r--tools/perf/tests/mmap-thread-lookup.c2
-rw-r--r--tools/perf/tests/sample-parsing.c1
-rw-r--r--tools/perf/tests/switch-tracking.c3
-rw-r--r--tools/perf/tests/thread-map.c3
-rw-r--r--tools/perf/tests/vmlinux-kallsyms.c1
-rw-r--r--tools/perf/ui/browser.c2
-rw-r--r--tools/perf/ui/browser.h1
-rw-r--r--tools/perf/ui/browsers/annotate.c2
-rw-r--r--tools/perf/ui/browsers/hists.c17
-rw-r--r--tools/perf/ui/browsers/map.c1
-rw-r--r--tools/perf/ui/browsers/res_sample.c6
-rw-r--r--tools/perf/ui/browsers/scripts.c4
-rw-r--r--tools/perf/ui/gtk/annotate.c2
-rw-r--r--tools/perf/ui/gtk/util.c3
-rw-r--r--tools/perf/ui/stdio/hist.c2
-rw-r--r--tools/perf/ui/tui/setup.c1
-rw-r--r--tools/perf/ui/tui/util.c2
-rw-r--r--tools/perf/util/Build5
-rw-r--r--tools/perf/util/annotate.c13
-rw-r--r--tools/perf/util/arm-spe.c2
-rw-r--r--tools/perf/util/auxtrace.c11
-rw-r--r--tools/perf/util/bpf-loader.c3
-rw-r--r--tools/perf/util/build-id.c1
-rw-r--r--tools/perf/util/call-path.c5
-rw-r--r--tools/perf/util/callchain.c12
-rw-r--r--tools/perf/util/cgroup.c4
-rw-r--r--tools/perf/util/comm.c2
-rw-r--r--tools/perf/util/config.c3
-rw-r--r--tools/perf/util/counts.c2
-rw-r--r--tools/perf/util/cpumap.c2
-rw-r--r--tools/perf/util/cputopo.c5
-rw-r--r--tools/perf/util/cs-etm-decoder/cs-etm-decoder.c1
-rw-r--r--tools/perf/util/cs-etm.c8
-rw-r--r--tools/perf/util/data-convert-bt.c4
-rw-r--r--tools/perf/util/data.c3
-rw-r--r--tools/perf/util/db-export.c7
-rw-r--r--tools/perf/util/debug.c1
-rw-r--r--tools/perf/util/demangle-java.c3
-rw-r--r--tools/perf/util/dso.c5
-rw-r--r--tools/perf/util/dwarf-aux.c2
-rw-r--r--tools/perf/util/env.c11
-rw-r--r--tools/perf/util/event.c3
-rw-r--r--tools/perf/util/evlist.c2
-rw-r--r--tools/perf/util/evsel.c4
-rw-r--r--tools/perf/util/get_current_dir_name.c6
-rw-r--r--tools/perf/util/get_current_dir_name.h8
-rw-r--r--tools/perf/util/header.c8
-rw-r--r--tools/perf/util/help-unknown-cmd.c2
-rw-r--r--tools/perf/util/hist.c20
-rw-r--r--tools/perf/util/intel-bts.c7
-rw-r--r--tools/perf/util/intel-pt-decoder/intel-pt-decoder.c2
-rw-r--r--tools/perf/util/intel-pt.c15
-rw-r--r--tools/perf/util/jitdump.c7
-rw-r--r--tools/perf/util/llvm-utils.c4
-rw-r--r--tools/perf/util/machine.c6
-rw-r--r--tools/perf/util/map.c9
-rw-r--r--tools/perf/util/mem2node.c2
-rw-r--r--tools/perf/util/metricgroup.c10
-rw-r--r--tools/perf/util/mmap.c1
-rw-r--r--tools/perf/util/namespaces.c3
-rw-r--r--tools/perf/util/namespaces.h4
-rw-r--r--tools/perf/util/ordered-events.c6
-rw-r--r--tools/perf/util/parse-branch-options.c2
-rw-r--r--tools/perf/util/parse-events.c3
-rw-r--r--tools/perf/util/parse-events.y2
-rw-r--r--tools/perf/util/parse-regs-options.c8
-rw-r--r--tools/perf/util/pmu.c4
-rw-r--r--tools/perf/util/probe-event.c55
-rw-r--r--tools/perf/util/probe-file.c2
-rw-r--r--tools/perf/util/probe-finder.c2
-rw-r--r--tools/perf/util/pstack.c2
-rw-r--r--tools/perf/util/python-ext-sources1
-rw-r--r--tools/perf/util/s390-cpumsf.c11
-rw-r--r--tools/perf/util/session.c7
-rw-r--r--tools/perf/util/setns.c4
-rw-r--r--tools/perf/util/srccode.c11
-rw-r--r--tools/perf/util/srcline.c2
-rw-r--r--tools/perf/util/stat-shadow.c3
-rw-r--r--tools/perf/util/stat.c3
-rw-r--r--tools/perf/util/strbuf.c3
-rw-r--r--tools/perf/util/strfilter.c3
-rw-r--r--tools/perf/util/strlist.c2
-rw-r--r--tools/perf/util/svghelper.c2
-rw-r--r--tools/perf/util/symbol-elf.c18
-rw-r--r--tools/perf/util/symbol-minimal.c3
-rw-r--r--tools/perf/util/symbol.c1
-rw-r--r--tools/perf/util/syscalltbl.c2
-rw-r--r--tools/perf/util/target.c2
-rw-r--r--tools/perf/util/thread-stack.c3
-rw-r--r--tools/perf/util/thread.c6
-rw-r--r--tools/perf/util/thread_map.c4
-rw-r--r--tools/perf/util/trace-event-info.c1
-rw-r--r--tools/perf/util/trace-event-scripting.c2
-rw-r--r--tools/perf/util/unwind-libdw.c1
-rw-r--r--tools/perf/util/unwind-libunwind-local.c3
-rw-r--r--tools/perf/util/usage.c3
-rw-r--r--tools/perf/util/util.h17
-rw-r--r--tools/perf/util/values.c2
-rw-r--r--tools/perf/util/vdso.c1
-rw-r--r--tools/perf/util/xyarray.c2
-rw-r--r--tools/power/x86/intel-speed-select/.gitignore2
-rw-r--r--tools/power/x86/intel-speed-select/Build1
-rw-r--r--tools/power/x86/intel-speed-select/Makefile56
-rw-r--r--tools/power/x86/intel-speed-select/isst-config.c1607
-rw-r--r--tools/power/x86/intel-speed-select/isst-core.c721
-rw-r--r--tools/power/x86/intel-speed-select/isst-display.c479
-rw-r--r--tools/power/x86/intel-speed-select/isst.h231
-rw-r--r--tools/testing/nvdimm/test/iomap.c57
303 files changed, 8540 insertions, 2628 deletions
diff --git a/Documentation/ABI/stable/sysfs-driver-mlxreg-io b/Documentation/ABI/stable/sysfs-driver-mlxreg-io
index 3544968f43cc..8ca498447aeb 100644
--- a/Documentation/ABI/stable/sysfs-driver-mlxreg-io
+++ b/Documentation/ABI/stable/sysfs-driver-mlxreg-io
@@ -120,3 +120,23 @@ Description: These files show the system reset cause, as following: ComEx
the last reset cause.
The files are read only.
+
+Date: June 2019
+KernelVersion: 5.3
+Contact: Vadim Pasternak <vadimpmellanox.com>
+Description: These files show the system reset cause, as following:
+ COMEX thermal shutdown; wathchdog power off or reset was derived
+ by one of the next components: COMEX, switch board or by Small Form
+ Factor mezzanine, reset requested from ASIC, reset cuased by BIOS
+ reload. Value 1 in file means this is reset cause, 0 - otherwise.
+ Only one of the above causes could be 1 at the same time, representing
+ only last reset cause.
+
+ The files are read only.
+
+What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_comex_thermal
+What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_comex_wd
+What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_from_asic
+What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_reload_bios
+What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_sff_wd
+What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_swb_wd
diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi
index 019e1e29370e..87ae5cc983bf 100644
--- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
+++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
@@ -36,3 +36,13 @@ KernelVersion: 3.5
Contact: "AceLan Kao" <acelan.kao@canonical.com>
Description:
Resume on lid open. 1 means on, 0 means off.
+
+What: /sys/devices/platform/<platform>/fan_mode
+Date: Apr 2019
+KernelVersion: 5.2
+Contact: "Yurii Pavlovskyi" <yurii.pavlovskyi@gmail.com>
+Description:
+ Fan boost mode:
+ * 0 - normal,
+ * 1 - overboost,
+ * 2 - silent
diff --git a/Documentation/devicetree/bindings/mailbox/omap-mailbox.txt b/Documentation/devicetree/bindings/mailbox/omap-mailbox.txt
index 0ef372656a3e..35c3f56b7f7b 100644
--- a/Documentation/devicetree/bindings/mailbox/omap-mailbox.txt
+++ b/Documentation/devicetree/bindings/mailbox/omap-mailbox.txt
@@ -1,4 +1,4 @@
-OMAP2+ Mailbox Driver
+OMAP2+ and K3 Mailbox
=====================
The OMAP mailbox hardware facilitates communication between different processors
@@ -7,7 +7,7 @@ various processor subsystems and is connected on an interconnect bus. The
communication is achieved through a set of registers for message storage and
interrupt configuration registers.
-Each mailbox IP block has a certain number of h/w fifo queues and output
+Each mailbox IP block/cluster has a certain number of h/w fifo queues and output
interrupt lines. An output interrupt line is routed to an interrupt controller
within a processor subsystem, and there can be more than one line going to a
specific processor's interrupt controller. The interrupt line connections are
@@ -23,12 +23,16 @@ All the current OMAP SoCs except for the newest DRA7xx SoC has a single IP
instance. DRA7xx has multiple instances with different number of h/w fifo queues
and interrupt lines between different instances. The interrupt lines can also be
routed to different processor sub-systems on DRA7xx as they are routed through
-the Crossbar, a kind of interrupt router/multiplexer.
+the Crossbar, a kind of interrupt router/multiplexer. The K3 AM65x and J721E
+SoCs has each of these instances form a cluster and combine multiple clusters
+into a single IP block present within the Main NavSS. The interrupt lines from
+all these clusters are multiplexed and routed to different processor subsystems
+over a limited number of common interrupt output lines of an Interrupt Router.
Mailbox Device Node:
====================
-A Mailbox device node is used to represent a Mailbox IP instance within a SoC.
-The sub-mailboxes are represented as child nodes of this parent node.
+A Mailbox device node is used to represent a Mailbox IP instance/cluster within
+a SoC. The sub-mailboxes are represented as child nodes of this parent node.
Required properties:
--------------------
@@ -37,12 +41,12 @@ Required properties:
"ti,omap3-mailbox" for OMAP3430, OMAP3630 SoCs
"ti,omap4-mailbox" for OMAP44xx, OMAP54xx, AM33xx,
AM43xx and DRA7xx SoCs
+ "ti,am654-mailbox" for K3 AM65x and J721E SoCs
- reg: Contains the mailbox register address range (base
address and length)
- interrupts: Contains the interrupt information for the mailbox
device. The format is dependent on which interrupt
- controller the OMAP device uses
-- ti,hwmods: Name of the hwmod associated with the mailbox
+ controller the Mailbox device uses
- #mbox-cells: Common mailbox binding property to identify the number
of cells required for the mailbox specifier. Should be
1
@@ -50,6 +54,23 @@ Required properties:
device can interrupt
- ti,mbox-num-fifos: Number of h/w fifo queues within the mailbox IP block
+SoC-specific Required properties:
+---------------------------------
+The following are mandatory properties for the OMAP architecture based SoCs
+only:
+- ti,hwmods: Name of the hwmod associated with the mailbox. This
+ should be defined in the mailbox node only if the node
+ is not defined as a child node of a corresponding sysc
+ interconnect node.
+
+The following are mandatory properties for the K3 AM65x and J721E SoCs only:
+- interrupt-parent: Should contain a phandle to the TI-SCI interrupt
+ controller node that is used to dynamically program
+ the interrupt routes between the IP and the main GIC
+ controllers. See the following binding for additional
+ details,
+ Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt
+
Child Nodes:
============
A child node is used for representing the actual sub-mailbox device that is
@@ -98,7 +119,7 @@ to be used by the client user.
Example:
--------
-/* OMAP4 */
+1. /* OMAP4 */
mailbox: mailbox@4a0f4000 {
compatible = "ti,omap4-mailbox";
reg = <0x4a0f4000 0x200>;
@@ -123,7 +144,7 @@ dsp {
...
};
-/* AM33xx */
+2. /* AM33xx */
mailbox: mailbox@480c8000 {
compatible = "ti,omap4-mailbox";
reg = <0x480C8000 0x200>;
@@ -137,3 +158,23 @@ mailbox: mailbox@480c8000 {
ti,mbox-rx = <0 0 3>;
};
};
+
+3. /* AM65x */
+&cbass_main {
+ cbass_main_navss: interconnect0 {
+ mailbox0_cluster0: mailbox@31f80000 {
+ compatible = "ti,am654-mailbox";
+ reg = <0x00 0x31f80000 0x00 0x200>;
+ #mbox-cells = <1>;
+ ti,mbox-num-users = <4>;
+ ti,mbox-num-fifos = <16>;
+ interrupt-parent = <&intr_main_navss>;
+ interrupts = <164 0>;
+
+ mbox_mcu_r5fss0_core0: mbox-mcu-r5fss0-core0 {
+ ti,mbox-tx = <1 0 0>;
+ ti,mbox-rx = <0 0 0>;
+ };
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/misc/olpc,xo1.75-ec.txt b/Documentation/devicetree/bindings/misc/olpc,xo1.75-ec.txt
new file mode 100644
index 000000000000..8c4d649cdd8f
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/olpc,xo1.75-ec.txt
@@ -0,0 +1,23 @@
+OLPC XO-1.75 Embedded Controller
+
+Required properties:
+- compatible: Should be "olpc,xo1.75-ec".
+- cmd-gpios: gpio specifier of the CMD pin
+
+The embedded controller requires the SPI controller driver to signal readiness
+to receive a transfer (that is, when TX FIFO contains the response data) by
+strobing the ACK pin with the ready signal. See the "ready-gpios" property of the
+SSP binding as documented in:
+<Documentation/devicetree/bindings/spi/spi-pxa2xx.txt>.
+
+Example:
+ &ssp3 {
+ spi-slave;
+ ready-gpios = <&gpio 125 GPIO_ACTIVE_HIGH>;
+
+ slave {
+ compatible = "olpc,xo1.75-ec";
+ spi-cpha;
+ cmd-gpios = <&gpio 155 GPIO_ACTIVE_HIGH>;
+ };
+ };
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index c9558146ac58..ab0b3f686454 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -348,3 +348,4 @@ Code Seq#(hex) Include File Comments
0xF6 all LTTng Linux Trace Toolkit Next Generation
<mailto:mathieu.desnoyers@efficios.com>
0xFD all linux/dm-ioctl.h
+0xFE all linux/isst_if.h
diff --git a/Documentation/vm/hmm.rst b/Documentation/vm/hmm.rst
index 7cdf7282e022..7d90964abbb0 100644
--- a/Documentation/vm/hmm.rst
+++ b/Documentation/vm/hmm.rst
@@ -10,7 +10,7 @@ of this being specialized struct page for such memory (see sections 5 to 7 of
this document).
HMM also provides optional helpers for SVM (Share Virtual Memory), i.e.,
-allowing a device to transparently access program address coherently with
+allowing a device to transparently access program addresses coherently with
the CPU meaning that any valid pointer on the CPU is also a valid pointer
for the device. This is becoming mandatory to simplify the use of advanced
heterogeneous computing where GPU, DSP, or FPGA are used to perform various
@@ -22,8 +22,8 @@ expose the hardware limitations that are inherent to many platforms. The third
section gives an overview of the HMM design. The fourth section explains how
CPU page-table mirroring works and the purpose of HMM in this context. The
fifth section deals with how device memory is represented inside the kernel.
-Finally, the last section presents a new migration helper that allows lever-
-aging the device DMA engine.
+Finally, the last section presents a new migration helper that allows
+leveraging the device DMA engine.
.. contents:: :local:
@@ -39,20 +39,20 @@ address space. I use shared address space to refer to the opposite situation:
i.e., one in which any application memory region can be used by a device
transparently.
-Split address space happens because device can only access memory allocated
-through device specific API. This implies that all memory objects in a program
+Split address space happens because devices can only access memory allocated
+through a device specific API. This implies that all memory objects in a program
are not equal from the device point of view which complicates large programs
that rely on a wide set of libraries.
-Concretely this means that code that wants to leverage devices like GPUs needs
-to copy object between generically allocated memory (malloc, mmap private, mmap
+Concretely, this means that code that wants to leverage devices like GPUs needs
+to copy objects between generically allocated memory (malloc, mmap private, mmap
share) and memory allocated through the device driver API (this still ends up
with an mmap but of the device file).
For flat data sets (array, grid, image, ...) this isn't too hard to achieve but
-complex data sets (list, tree, ...) are hard to get right. Duplicating a
+for complex data sets (list, tree, ...) it's hard to get right. Duplicating a
complex data set needs to re-map all the pointer relations between each of its
-elements. This is error prone and program gets harder to debug because of the
+elements. This is error prone and programs get harder to debug because of the
duplicate data set and addresses.
Split address space also means that libraries cannot transparently use data
@@ -77,12 +77,12 @@ I/O bus, device memory characteristics
I/O buses cripple shared address spaces due to a few limitations. Most I/O
buses only allow basic memory access from device to main memory; even cache
-coherency is often optional. Access to device memory from CPU is even more
+coherency is often optional. Access to device memory from a CPU is even more
limited. More often than not, it is not cache coherent.
If we only consider the PCIE bus, then a device can access main memory (often
through an IOMMU) and be cache coherent with the CPUs. However, it only allows
-a limited set of atomic operations from device on main memory. This is worse
+a limited set of atomic operations from the device on main memory. This is worse
in the other direction: the CPU can only access a limited range of the device
memory and cannot perform atomic operations on it. Thus device memory cannot
be considered the same as regular memory from the kernel point of view.
@@ -93,20 +93,20 @@ The final limitation is latency. Access to main memory from the device has an
order of magnitude higher latency than when the device accesses its own memory.
Some platforms are developing new I/O buses or additions/modifications to PCIE
-to address some of these limitations (OpenCAPI, CCIX). They mainly allow two-
-way cache coherency between CPU and device and allow all atomic operations the
+to address some of these limitations (OpenCAPI, CCIX). They mainly allow
+two-way cache coherency between CPU and device and allow all atomic operations the
architecture supports. Sadly, not all platforms are following this trend and
some major architectures are left without hardware solutions to these problems.
So for shared address space to make sense, not only must we allow devices to
access any memory but we must also permit any memory to be migrated to device
-memory while device is using it (blocking CPU access while it happens).
+memory while the device is using it (blocking CPU access while it happens).
Shared address space and migration
==================================
-HMM intends to provide two main features. First one is to share the address
+HMM intends to provide two main features. The first one is to share the address
space by duplicating the CPU page table in the device page table so the same
address points to the same physical memory for any valid main memory address in
the process address space.
@@ -121,14 +121,14 @@ why HMM provides helpers to factor out everything that can be while leaving the
hardware specific details to the device driver.
The second mechanism HMM provides is a new kind of ZONE_DEVICE memory that
-allows allocating a struct page for each page of the device memory. Those pages
+allows allocating a struct page for each page of device memory. Those pages
are special because the CPU cannot map them. However, they allow migrating
main memory to device memory using existing migration mechanisms and everything
-looks like a page is swapped out to disk from the CPU point of view. Using a
-struct page gives the easiest and cleanest integration with existing mm mech-
-anisms. Here again, HMM only provides helpers, first to hotplug new ZONE_DEVICE
+looks like a page that is swapped out to disk from the CPU point of view. Using a
+struct page gives the easiest and cleanest integration with existing mm
+mechanisms. Here again, HMM only provides helpers, first to hotplug new ZONE_DEVICE
memory for the device memory and second to perform migration. Policy decisions
-of what and when to migrate things is left to the device driver.
+of what and when to migrate is left to the device driver.
Note that any CPU access to a device page triggers a page fault and a migration
back to main memory. For example, when a page backing a given CPU address A is
@@ -136,8 +136,8 @@ migrated from a main memory page to a device page, then any CPU access to
address A triggers a page fault and initiates a migration back to main memory.
With these two features, HMM not only allows a device to mirror process address
-space and keeping both CPU and device page table synchronized, but also lever-
-ages device memory by migrating the part of the data set that is actively being
+space and keeps both CPU and device page tables synchronized, but also
+leverages device memory by migrating the part of the data set that is actively being
used by the device.
@@ -151,21 +151,28 @@ registration of an hmm_mirror struct::
int hmm_mirror_register(struct hmm_mirror *mirror,
struct mm_struct *mm);
- int hmm_mirror_register_locked(struct hmm_mirror *mirror,
- struct mm_struct *mm);
-
-The locked variant is to be used when the driver is already holding mmap_sem
-of the mm in write mode. The mirror struct has a set of callbacks that are used
+The mirror struct has a set of callbacks that are used
to propagate CPU page tables::
struct hmm_mirror_ops {
+ /* release() - release hmm_mirror
+ *
+ * @mirror: pointer to struct hmm_mirror
+ *
+ * This is called when the mm_struct is being released. The callback
+ * must ensure that all access to any pages obtained from this mirror
+ * is halted before the callback returns. All future access should
+ * fault.
+ */
+ void (*release)(struct hmm_mirror *mirror);
+
/* sync_cpu_device_pagetables() - synchronize page tables
*
* @mirror: pointer to struct hmm_mirror
- * @update_type: type of update that occurred to the CPU page table
- * @start: virtual start address of the range to update
- * @end: virtual end address of the range to update
+ * @update: update information (see struct mmu_notifier_range)
+ * Return: -EAGAIN if update.blockable false and callback need to
+ * block, 0 otherwise.
*
* This callback ultimately originates from mmu_notifiers when the CPU
* page table is updated. The device driver must update its page table
@@ -176,14 +183,12 @@ to propagate CPU page tables::
* page tables are completely updated (TLBs flushed, etc); this is a
* synchronous call.
*/
- void (*update)(struct hmm_mirror *mirror,
- enum hmm_update action,
- unsigned long start,
- unsigned long end);
+ int (*sync_cpu_device_pagetables)(struct hmm_mirror *mirror,
+ const struct hmm_update *update);
};
The device driver must perform the update action to the range (mark range
-read only, or fully unmap, ...). The device must be done with the update before
+read only, or fully unmap, etc.). The device must complete the update before
the driver callback returns.
When the device driver wants to populate a range of virtual addresses, it can
@@ -194,17 +199,18 @@ use either::
The first one (hmm_range_snapshot()) will only fetch present CPU page table
entries and will not trigger a page fault on missing or non-present entries.
-The second one does trigger a page fault on missing or read-only entry if the
-write parameter is true. Page faults use the generic mm page fault code path
-just like a CPU page fault.
+The second one does trigger a page fault on missing or read-only entries if
+write access is requested (see below). Page faults use the generic mm page
+fault code path just like a CPU page fault.
Both functions copy CPU page table entries into their pfns array argument. Each
entry in that array corresponds to an address in the virtual range. HMM
provides a set of flags to help the driver identify special CPU page table
entries.
-Locking with the update() callback is the most important aspect the driver must
-respect in order to keep things properly synchronized. The usage pattern is::
+Locking within the sync_cpu_device_pagetables() callback is the most important
+aspect the driver must respect in order to keep things properly synchronized.
+The usage pattern is::
int driver_populate_range(...)
{
@@ -239,11 +245,11 @@ respect in order to keep things properly synchronized. The usage pattern is::
hmm_range_wait_until_valid(&range, TIMEOUT_IN_MSEC);
goto again;
}
- hmm_mirror_unregister(&range);
+ hmm_range_unregister(&range);
return ret;
}
take_lock(driver->update);
- if (!range.valid) {
+ if (!hmm_range_valid(&range)) {
release_lock(driver->update);
up_read(&mm->mmap_sem);
goto again;
@@ -251,15 +257,15 @@ respect in order to keep things properly synchronized. The usage pattern is::
// Use pfns array content to update device page table
- hmm_mirror_unregister(&range);
+ hmm_range_unregister(&range);
release_lock(driver->update);
up_read(&mm->mmap_sem);
return 0;
}
The driver->update lock is the same lock that the driver takes inside its
-update() callback. That lock must be held before checking the range.valid
-field to avoid any race with a concurrent CPU page table update.
+sync_cpu_device_pagetables() callback. That lock must be held before calling
+hmm_range_valid() to avoid any race with a concurrent CPU page table update.
HMM implements all this on top of the mmu_notifier API because we wanted a
simpler API and also to be able to perform optimizations latter on like doing
@@ -279,46 +285,47 @@ concurrently).
Leverage default_flags and pfn_flags_mask
=========================================
-The hmm_range struct has 2 fields default_flags and pfn_flags_mask that allows
-to set fault or snapshot policy for a whole range instead of having to set them
-for each entries in the range.
+The hmm_range struct has 2 fields, default_flags and pfn_flags_mask, that specify
+fault or snapshot policy for the whole range instead of having to set them
+for each entry in the pfns array.
+
+For instance, if the device flags for range.flags are::
-For instance if the device flags for device entries are:
- VALID (1 << 63)
- WRITE (1 << 62)
+ range.flags[HMM_PFN_VALID] = (1 << 63);
+ range.flags[HMM_PFN_WRITE] = (1 << 62);
-Now let say that device driver wants to fault with at least read a range then
-it does set::
+and the device driver wants pages for a range with at least read permission,
+it sets::
range->default_flags = (1 << 63);
range->pfn_flags_mask = 0;
-and calls hmm_range_fault() as described above. This will fill fault all page
+and calls hmm_range_fault() as described above. This will fill fault all pages
in the range with at least read permission.
-Now let say driver wants to do the same except for one page in the range for
-which its want to have write. Now driver set::
+Now let's say the driver wants to do the same except for one page in the range for
+which it wants to have write permission. Now driver set::
range->default_flags = (1 << 63);
range->pfn_flags_mask = (1 << 62);
range->pfns[index_of_write] = (1 << 62);
-With this HMM will fault in all page with at least read (ie valid) and for the
+With this, HMM will fault in all pages with at least read (i.e., valid) and for the
address == range->start + (index_of_write << PAGE_SHIFT) it will fault with
-write permission ie if the CPU pte does not have write permission set then HMM
+write permission i.e., if the CPU pte does not have write permission set then HMM
will call handle_mm_fault().
-Note that HMM will populate the pfns array with write permission for any entry
-that have write permission within the CPU pte no matter what are the values set
+Note that HMM will populate the pfns array with write permission for any page
+that is mapped with CPU write permission no matter what values are set
in default_flags or pfn_flags_mask.
Represent and manage device memory from core kernel point of view
=================================================================
-Several different designs were tried to support device memory. First one used
-a device specific data structure to keep information about migrated memory and
-HMM hooked itself in various places of mm code to handle any access to
+Several different designs were tried to support device memory. The first one
+used a device specific data structure to keep information about migrated memory
+and HMM hooked itself in various places of mm code to handle any access to
addresses that were backed by device memory. It turns out that this ended up
replicating most of the fields of struct page and also needed many kernel code
paths to be updated to understand this new kind of memory.
@@ -329,33 +336,6 @@ directly using struct page for device memory which left most kernel code paths
unaware of the difference. We only need to make sure that no one ever tries to
map those pages from the CPU side.
-HMM provides a set of helpers to register and hotplug device memory as a new
-region needing a struct page. This is offered through a very simple API::
-
- struct hmm_devmem *hmm_devmem_add(const struct hmm_devmem_ops *ops,
- struct device *device,
- unsigned long size);
- void hmm_devmem_remove(struct hmm_devmem *devmem);
-
-The hmm_devmem_ops is where most of the important things are::
-
- struct hmm_devmem_ops {
- void (*free)(struct hmm_devmem *devmem, struct page *page);
- int (*fault)(struct hmm_devmem *devmem,
- struct vm_area_struct *vma,
- unsigned long addr,
- struct page *page,
- unsigned flags,
- pmd_t *pmdp);
- };
-
-The first callback (free()) happens when the last reference on a device page is
-dropped. This means the device page is now free and no longer used by anyone.
-The second callback happens whenever the CPU tries to access a device page
-which it cannot do. This second callback must trigger a migration back to
-system memory.
-
-
Migration to and from device memory
===================================
@@ -417,9 +397,9 @@ willing to pay to keep all the code simpler.
Memory cgroup (memcg) and rss accounting
========================================
-For now device memory is accounted as any regular page in rss counters (either
+For now, device memory is accounted as any regular page in rss counters (either
anonymous if device page is used for anonymous, file if device page is used for
-file backed page or shmem if device page is used for shared memory). This is a
+file backed page, or shmem if device page is used for shared memory). This is a
deliberate choice to keep existing applications, that might start using device
memory without knowing about it, running unimpacted.
@@ -439,6 +419,6 @@ get more experience in how device memory is used and its impact on memory
resource control.
-Note that device memory can never be pinned by device driver nor through GUP
+Note that device memory can never be pinned by a device driver nor through GUP
and thus such memory is always free upon process exit. Or when last reference
is dropped in case of shared memory or file backed memory.
diff --git a/MAINTAINERS b/MAINTAINERS
index 211ea3a199bd..f5533d1bda2e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8246,6 +8246,14 @@ S: Supported
F: drivers/infiniband/hw/i40iw/
F: include/uapi/rdma/i40iw-abi.h
+INTEL SPEED SELECT TECHNOLOGY
+M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/intel_speed_select_if/
+F: tools/power/x86/intel-speed-select/
+F: include/uapi/linux/isst_if.h
+
INTEL TELEMETRY DRIVER
M: Rajneesh Bhardwaj <rajneesh.bhardwaj@linux.intel.com>
M: "David E. Box" <david.e.box@linux.intel.com>
diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c
index 6d5f0fc76666..26a8da3723bb 100644
--- a/arch/powerpc/mm/mem.c
+++ b/arch/powerpc/mm/mem.c
@@ -131,17 +131,9 @@ void __ref arch_remove_memory(int nid, u64 start, u64 size,
{
unsigned long start_pfn = start >> PAGE_SHIFT;
unsigned long nr_pages = size >> PAGE_SHIFT;
- struct page *page;
+ struct page *page = pfn_to_page(start_pfn) + vmem_altmap_offset(altmap);
int ret;
- /*
- * If we have an altmap then we need to skip over any reserved PFNs
- * when querying the zone.
- */
- page = pfn_to_page(start_pfn);
- if (altmap)
- page += vmem_altmap_offset(altmap);
-
__remove_pages(page_zone(page), start_pfn, nr_pages, altmap);
/* Remove htab bolted mappings for this section of memory */
diff --git a/arch/um/Kconfig b/arch/um/Kconfig
index 6b6eb938fcc1..3c3adfc486f2 100644
--- a/arch/um/Kconfig
+++ b/arch/um/Kconfig
@@ -184,6 +184,18 @@ config SECCOMP
If unsure, say Y.
+config UML_TIME_TRAVEL_SUPPORT
+ bool
+ prompt "Support time-travel mode (e.g. for test execution)"
+ help
+ Enable this option to support time travel inside the UML instance.
+
+ After enabling this option, two modes are accessible at runtime
+ (selected by the kernel command line), see the kernel's command-
+ line help for more details.
+
+ It is safe to say Y, but you probably don't need this.
+
endmenu
source "arch/um/drivers/Kconfig"
diff --git a/arch/um/configs/i386_defconfig b/arch/um/configs/i386_defconfig
index 8f114e3b0a7a..73e98bb57bf5 100644
--- a/arch/um/configs/i386_defconfig
+++ b/arch/um/configs/i386_defconfig
@@ -36,7 +36,6 @@ CONFIG_XTERM_CHAN=y
CONFIG_CON_CHAN="pts"
CONFIG_SSL_CHAN="pts"
CONFIG_UML_SOUND=m
-CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
CONFIG_BLK_DEV_UBD=y
diff --git a/arch/um/configs/x86_64_defconfig b/arch/um/configs/x86_64_defconfig
index 5d0875fc0db2..3281d7600225 100644
--- a/arch/um/configs/x86_64_defconfig
+++ b/arch/um/configs/x86_64_defconfig
@@ -34,7 +34,6 @@ CONFIG_XTERM_CHAN=y
CONFIG_CON_CHAN="pts"
CONFIG_SSL_CHAN="pts"
CONFIG_UML_SOUND=m
-CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
CONFIG_BLK_DEV_UBD=y
diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c
index a4e64edb8f38..749d2bf59599 100644
--- a/arch/um/drivers/chan_kern.c
+++ b/arch/um/drivers/chan_kern.c
@@ -171,19 +171,55 @@ int enable_chan(struct line *line)
return err;
}
+/* Items are added in IRQ context, when free_irq can't be called, and
+ * removed in process context, when it can.
+ * This handles interrupt sources which disappear, and which need to
+ * be permanently disabled. This is discovered in IRQ context, but
+ * the freeing of the IRQ must be done later.
+ */
+static DEFINE_SPINLOCK(irqs_to_free_lock);
+static LIST_HEAD(irqs_to_free);
+
+void free_irqs(void)
+{
+ struct chan *chan;
+ LIST_HEAD(list);
+ struct list_head *ele;
+ unsigned long flags;
+
+ spin_lock_irqsave(&irqs_to_free_lock, flags);
+ list_splice_init(&irqs_to_free, &list);
+ spin_unlock_irqrestore(&irqs_to_free_lock, flags);
+
+ list_for_each(ele, &list) {
+ chan = list_entry(ele, struct chan, free_list);
+
+ if (chan->input && chan->enabled)
+ um_free_irq(chan->line->driver->read_irq, chan);
+ if (chan->output && chan->enabled)
+ um_free_irq(chan->line->driver->write_irq, chan);
+ chan->enabled = 0;
+ }
+}
+
static void close_one_chan(struct chan *chan, int delay_free_irq)
{
+ unsigned long flags;
+
if (!chan->opened)
return;
- /* we can safely call free now - it will be marked
- * as free and freed once the IRQ stopped processing
- */
- if (chan->input && chan->enabled)
- um_free_irq(chan->line->driver->read_irq, chan);
- if (chan->output && chan->enabled)
- um_free_irq(chan->line->driver->write_irq, chan);
- chan->enabled = 0;
+ if (delay_free_irq) {
+ spin_lock_irqsave(&irqs_to_free_lock, flags);
+ list_add(&chan->free_list, &irqs_to_free);
+ spin_unlock_irqrestore(&irqs_to_free_lock, flags);
+ } else {
+ if (chan->input && chan->enabled)
+ um_free_irq(chan->line->driver->read_irq, chan);
+ if (chan->output && chan->enabled)
+ um_free_irq(chan->line->driver->write_irq, chan);
+ chan->enabled = 0;
+ }
if (chan->ops->close != NULL)
(*chan->ops->close)(chan->fd, chan->data);
diff --git a/arch/um/drivers/ssl.c b/arch/um/drivers/ssl.c
index b8d14fa52059..7ae407d5337e 100644
--- a/arch/um/drivers/ssl.c
+++ b/arch/um/drivers/ssl.c
@@ -12,7 +12,6 @@
#include <linux/console.h>
#include <asm/termbits.h>
#include <asm/irq.h>
-#include "ssl.h"
#include "chan.h"
#include <init.h>
#include <irq_user.h>
diff --git a/arch/um/drivers/ssl.h b/arch/um/drivers/ssl.h
deleted file mode 100644
index 314d17725ce6..000000000000
--- a/arch/um/drivers/ssl.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
- * Licensed under the GPL
- */
-
-#ifndef __SSL_H__
-#define __SSL_H__
-
-extern int ssl_read(int fd, int line);
-extern void ssl_receive_char(int line, char ch);
-
-#endif
-
diff --git a/arch/um/include/asm/mmu_context.h b/arch/um/include/asm/mmu_context.h
index 9f4b4bb78120..00cefd33afdd 100644
--- a/arch/um/include/asm/mmu_context.h
+++ b/arch/um/include/asm/mmu_context.h
@@ -52,7 +52,7 @@ static inline void activate_mm(struct mm_struct *old, struct mm_struct *new)
* when the new ->mm is used for the first time.
*/
__switch_mm(&new->context.id);
- down_write(&new->mmap_sem);
+ down_write_nested(&new->mmap_sem, 1);
uml_setup_stubs(new);
up_write(&new->mmap_sem);
}
diff --git a/arch/um/include/shared/os.h b/arch/um/include/shared/os.h
index ebf23012a59b..4a62ac4251a5 100644
--- a/arch/um/include/shared/os.h
+++ b/arch/um/include/shared/os.h
@@ -250,15 +250,13 @@ extern void os_warn(const char *fmt, ...)
/* time.c */
extern void os_idle_sleep(unsigned long long nsecs);
-extern int os_timer_create(void* timer);
-extern int os_timer_set_interval(void* timer, void* its);
-extern int os_timer_one_shot(int ticks);
-extern long long os_timer_disable(void);
-extern long os_timer_remain(void* timer);
+extern int os_timer_create(void);
+extern int os_timer_set_interval(unsigned long long nsecs);
+extern int os_timer_one_shot(unsigned long long nsecs);
+extern void os_timer_disable(void);
extern void uml_idle_timer(void);
extern long long os_persistent_clock_emulation(void);
extern long long os_nsecs(void);
-extern long long os_vnsecs(void);
/* skas/mem.c */
extern long run_syscall_stub(struct mm_id * mm_idp,
diff --git a/arch/um/include/shared/timer-internal.h b/arch/um/include/shared/timer-internal.h
index 03e6f217f807..8574338bf23b 100644
--- a/arch/um/include/shared/timer-internal.h
+++ b/arch/um/include/shared/timer-internal.h
@@ -10,4 +10,52 @@
#define TIMER_MULTIPLIER 256
#define TIMER_MIN_DELTA 500
+enum time_travel_mode {
+ TT_MODE_OFF,
+ TT_MODE_BASIC,
+ TT_MODE_INFCPU,
+};
+
+enum time_travel_timer_mode {
+ TT_TMR_DISABLED,
+ TT_TMR_ONESHOT,
+ TT_TMR_PERIODIC,
+};
+
+#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
+extern enum time_travel_mode time_travel_mode;
+extern unsigned long long time_travel_time;
+extern enum time_travel_timer_mode time_travel_timer_mode;
+extern unsigned long long time_travel_timer_expiry;
+extern unsigned long long time_travel_timer_interval;
+
+static inline void time_travel_set_time(unsigned long long ns)
+{
+ time_travel_time = ns;
+}
+
+static inline void time_travel_set_timer(enum time_travel_timer_mode mode,
+ unsigned long long expiry)
+{
+ time_travel_timer_mode = mode;
+ time_travel_timer_expiry = expiry;
+}
+#else
+#define time_travel_mode TT_MODE_OFF
+#define time_travel_time 0
+#define time_travel_timer_expiry 0
+#define time_travel_timer_interval 0
+
+static inline void time_travel_set_time(unsigned long long ns)
+{
+}
+
+static inline void time_travel_set_timer(enum time_travel_timer_mode mode,
+ unsigned long long expiry)
+{
+}
+
+#define time_travel_timer_mode TT_TMR_DISABLED
+#endif
+
#endif
diff --git a/arch/um/kernel/irq.c b/arch/um/kernel/irq.c
index 598d7b3d9355..efde1f16c603 100644
--- a/arch/um/kernel/irq.c
+++ b/arch/um/kernel/irq.c
@@ -21,6 +21,8 @@
#include <irq_user.h>
+extern void free_irqs(void);
+
/* When epoll triggers we do not know why it did so
* we can also have different IRQs for read and write.
* This is why we keep a small irq_fd array for each fd -
@@ -100,6 +102,8 @@ void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
}
}
}
+
+ free_irqs();
}
static int assign_epoll_events_to_irq(struct irq_entry *irq_entry)
@@ -380,10 +384,8 @@ EXPORT_SYMBOL(deactivate_fd);
*/
int deactivate_all_fds(void)
{
- unsigned long flags;
struct irq_entry *to_free;
- spin_lock_irqsave(&irq_lock, flags);
/* Stop IO. The IRQ loop has no lock so this is our
* only way of making sure we are safe to dispose
* of all IRQ handlers
@@ -399,8 +401,7 @@ int deactivate_all_fds(void)
);
to_free = to_free->next;
}
- garbage_collect_irq_entries();
- spin_unlock_irqrestore(&irq_lock, flags);
+ /* don't garbage collect - we can no longer call kfree() here */
os_close_epoll_fd();
return 0;
}
diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c
index 691b83b10649..67c0d1a860e9 100644
--- a/arch/um/kernel/process.c
+++ b/arch/um/kernel/process.c
@@ -203,10 +203,50 @@ void initial_thread_cb(void (*proc)(void *), void *arg)
kmalloc_ok = save_kmalloc_ok;
}
+static void time_travel_sleep(unsigned long long duration)
+{
+ unsigned long long next = time_travel_time + duration;
+
+ if (time_travel_mode != TT_MODE_INFCPU)
+ os_timer_disable();
+
+ if (time_travel_timer_mode != TT_TMR_DISABLED ||
+ time_travel_timer_expiry < next) {
+ if (time_travel_timer_mode == TT_TMR_ONESHOT)
+ time_travel_set_timer(TT_TMR_DISABLED, 0);
+ /*
+ * time_travel_time will be adjusted in the timer
+ * IRQ handler so it works even when the signal
+ * comes from the OS timer
+ */
+ deliver_alarm();
+ } else {
+ time_travel_set_time(next);
+ }
+
+ if (time_travel_mode != TT_MODE_INFCPU) {
+ if (time_travel_timer_mode == TT_TMR_PERIODIC)
+ os_timer_set_interval(time_travel_timer_interval);
+ else if (time_travel_timer_mode == TT_TMR_ONESHOT)
+ os_timer_one_shot(time_travel_timer_expiry - next);
+ }
+}
+
+static void um_idle_sleep(void)
+{
+ unsigned long long duration = UM_NSEC_PER_SEC;
+
+ if (time_travel_mode != TT_MODE_OFF) {
+ time_travel_sleep(duration);
+ } else {
+ os_idle_sleep(duration);
+ }
+}
+
void arch_cpu_idle(void)
{
cpu_tasks[current_thread_info()->cpu].pid = os_getpid();
- os_idle_sleep(UM_NSEC_PER_SEC);
+ um_idle_sleep();
local_irq_enable();
}
diff --git a/arch/um/kernel/skas/Makefile b/arch/um/kernel/skas/Makefile
index 0b76d8869c94..5bd3edfcfedf 100644
--- a/arch/um/kernel/skas/Makefile
+++ b/arch/um/kernel/skas/Makefile
@@ -12,4 +12,6 @@ obj-y := clone.o mmu.o process.o syscall.o uaccess.o
CFLAGS_clone.o := $(CFLAGS_NO_HARDENING)
UNPROFILE_OBJS := clone.o
+KCOV_INSTRUMENT := n
+
include arch/um/scripts/Makefile.rules
diff --git a/arch/um/kernel/skas/syscall.c b/arch/um/kernel/skas/syscall.c
index b783ac87d98a..44bb10785075 100644
--- a/arch/um/kernel/skas/syscall.c
+++ b/arch/um/kernel/skas/syscall.c
@@ -10,12 +10,23 @@
#include <sysdep/ptrace.h>
#include <sysdep/ptrace_user.h>
#include <sysdep/syscalls.h>
+#include <shared/timer-internal.h>
void handle_syscall(struct uml_pt_regs *r)
{
struct pt_regs *regs = container_of(r, struct pt_regs, regs);
int syscall;
+ /*
+ * If we have infinite CPU resources, then make every syscall also a
+ * preemption point, since we don't have any other preemption in this
+ * case, and kernel threads would basically never run until userspace
+ * went to sleep, even if said userspace interacts with the kernel in
+ * various ways.
+ */
+ if (time_travel_mode == TT_MODE_INFCPU)
+ schedule();
+
/* Initialize the syscall number and default return value. */
UPT_SYSCALL_NR(r) = PT_SYSCALL_NR(r->gp);
PT_REGS_SET_SYSCALL_RETURN(regs, -ENOSYS);
diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c
index 0c572a48158e..6a051b078359 100644
--- a/arch/um/kernel/time.c
+++ b/arch/um/kernel/time.c
@@ -19,11 +19,29 @@
#include <kern_util.h>
#include <os.h>
#include <timer-internal.h>
+#include <shared/init.h>
+
+#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
+enum time_travel_mode time_travel_mode;
+unsigned long long time_travel_time;
+enum time_travel_timer_mode time_travel_timer_mode;
+unsigned long long time_travel_timer_expiry;
+unsigned long long time_travel_timer_interval;
+
+static bool time_travel_start_set;
+static unsigned long long time_travel_start;
+#else
+#define time_travel_start_set 0
+#define time_travel_start 0
+#endif
void timer_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
{
unsigned long flags;
+ if (time_travel_mode != TT_MODE_OFF)
+ time_travel_set_time(time_travel_timer_expiry);
+
local_irq_save(flags);
do_IRQ(TIMER_IRQ, regs);
local_irq_restore(flags);
@@ -31,26 +49,47 @@ void timer_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
static int itimer_shutdown(struct clock_event_device *evt)
{
- os_timer_disable();
+ if (time_travel_mode != TT_MODE_OFF)
+ time_travel_set_timer(TT_TMR_DISABLED, 0);
+
+ if (time_travel_mode != TT_MODE_INFCPU)
+ os_timer_disable();
+
return 0;
}
static int itimer_set_periodic(struct clock_event_device *evt)
{
- os_timer_set_interval(NULL, NULL);
+ unsigned long long interval = NSEC_PER_SEC / HZ;
+
+ if (time_travel_mode != TT_MODE_OFF)
+ time_travel_set_timer(TT_TMR_PERIODIC,
+ time_travel_time + interval);
+
+ if (time_travel_mode != TT_MODE_INFCPU)
+ os_timer_set_interval(interval);
+
return 0;
}
static int itimer_next_event(unsigned long delta,
struct clock_event_device *evt)
{
- return os_timer_one_shot(delta);
+ delta += 1;
+
+ if (time_travel_mode != TT_MODE_OFF)
+ time_travel_set_timer(TT_TMR_ONESHOT,
+ time_travel_time + delta);
+
+ if (time_travel_mode != TT_MODE_INFCPU)
+ return os_timer_one_shot(delta);
+
+ return 0;
}
static int itimer_one_shot(struct clock_event_device *evt)
{
- os_timer_one_shot(1);
- return 0;
+ return itimer_next_event(0, evt);
}
static struct clock_event_device timer_clockevent = {
@@ -87,6 +126,17 @@ static irqreturn_t um_timer(int irq, void *dev)
static u64 timer_read(struct clocksource *cs)
{
+ if (time_travel_mode != TT_MODE_OFF) {
+ /*
+ * We make reading the timer cost a bit so that we don't get
+ * stuck in loops that expect time to move more than the
+ * exact requested sleep amount, e.g. python's socket server,
+ * see https://bugs.python.org/issue37026.
+ */
+ time_travel_set_time(time_travel_time + TIMER_MULTIPLIER);
+ return time_travel_time / TIMER_MULTIPLIER;
+ }
+
return os_nsecs() / TIMER_MULTIPLIER;
}
@@ -107,7 +157,7 @@ static void __init um_timer_setup(void)
printk(KERN_ERR "register_timer : request_irq failed - "
"errno = %d\n", -err);
- err = os_timer_create(NULL);
+ err = os_timer_create();
if (err != 0) {
printk(KERN_ERR "creation of timer failed - errno = %d\n", -err);
return;
@@ -123,7 +173,12 @@ static void __init um_timer_setup(void)
void read_persistent_clock64(struct timespec64 *ts)
{
- long long nsecs = os_persistent_clock_emulation();
+ long long nsecs;
+
+ if (time_travel_start_set)
+ nsecs = time_travel_start + time_travel_time;
+ else
+ nsecs = os_persistent_clock_emulation();
set_normalized_timespec64(ts, nsecs / NSEC_PER_SEC,
nsecs % NSEC_PER_SEC);
@@ -134,3 +189,65 @@ void __init time_init(void)
timer_set_signal_handler();
late_time_init = um_timer_setup;
}
+
+#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
+unsigned long calibrate_delay_is_known(void)
+{
+ if (time_travel_mode == TT_MODE_INFCPU)
+ return 1;
+ return 0;
+}
+
+int setup_time_travel(char *str)
+{
+ if (strcmp(str, "=inf-cpu") == 0) {
+ time_travel_mode = TT_MODE_INFCPU;
+ timer_clockevent.name = "time-travel-timer-infcpu";
+ timer_clocksource.name = "time-travel-clock";
+ return 1;
+ }
+
+ if (!*str) {
+ time_travel_mode = TT_MODE_BASIC;
+ timer_clockevent.name = "time-travel-timer";
+ timer_clocksource.name = "time-travel-clock";
+ return 1;
+ }
+
+ return -EINVAL;
+}
+
+__setup("time-travel", setup_time_travel);
+__uml_help(setup_time_travel,
+"time-travel\n"
+"This option just enables basic time travel mode, in which the clock/timers\n"
+"inside the UML instance skip forward when there's nothing to do, rather than\n"
+"waiting for real time to elapse. However, instance CPU speed is limited by\n"
+"the real CPU speed, so e.g. a 10ms timer will always fire after ~10ms wall\n"
+"clock (but quicker when there's nothing to do).\n"
+"\n"
+"time-travel=inf-cpu\n"
+"This enables time travel mode with infinite processing power, in which there\n"
+"are no wall clock timers, and any CPU processing happens - as seen from the\n"
+"guest - instantly. This can be useful for accurate simulation regardless of\n"
+"debug overhead, physical CPU speed, etc. but is somewhat dangerous as it can\n"
+"easily lead to getting stuck (e.g. if anything in the system busy loops).\n");
+
+int setup_time_travel_start(char *str)
+{
+ int err;
+
+ err = kstrtoull(str, 0, &time_travel_start);
+ if (err)
+ return err;
+
+ time_travel_start_set = 1;
+ return 1;
+}
+
+__setup("time-travel-start", setup_time_travel_start);
+__uml_help(setup_time_travel_start,
+"time-travel-start=<seconds>\n"
+"Configure the UML instance's wall clock to start at this value rather than\n"
+"the host's wall clock at the time of UML boot.\n");
+#endif
diff --git a/arch/um/os-Linux/time.c b/arch/um/os-Linux/time.c
index 0e39b9978729..6d94ff52362c 100644
--- a/arch/um/os-Linux/time.c
+++ b/arch/um/os-Linux/time.c
@@ -26,11 +26,11 @@ static inline long long timeval_to_ns(const struct timeval *tv)
static inline long long timespec_to_ns(const struct timespec *ts)
{
- return ((long long) ts->tv_sec * UM_NSEC_PER_SEC) +
- ts->tv_nsec;
+ return ((long long) ts->tv_sec * UM_NSEC_PER_SEC) + ts->tv_nsec;
}
-long long os_persistent_clock_emulation (void) {
+long long os_persistent_clock_emulation(void)
+{
struct timespec realtime_tp;
clock_gettime(CLOCK_REALTIME, &realtime_tp);
@@ -40,94 +40,41 @@ long long os_persistent_clock_emulation (void) {
/**
* os_timer_create() - create an new posix (interval) timer
*/
-int os_timer_create(void* timer) {
-
- timer_t* t = timer;
-
- if(t == NULL) {
- t = &event_high_res_timer;
- }
+int os_timer_create(void)
+{
+ timer_t *t = &event_high_res_timer;
- if (timer_create(
- CLOCK_MONOTONIC,
- NULL,
- t) == -1) {
+ if (timer_create(CLOCK_MONOTONIC, NULL, t) == -1)
return -1;
- }
+
return 0;
}
-int os_timer_set_interval(void* timer, void* i)
+int os_timer_set_interval(unsigned long long nsecs)
{
struct itimerspec its;
- unsigned long long nsec;
- timer_t* t = timer;
- struct itimerspec* its_in = i;
-
- if(t == NULL) {
- t = &event_high_res_timer;
- }
- nsec = UM_NSEC_PER_SEC / UM_HZ;
+ its.it_value.tv_sec = nsecs / UM_NSEC_PER_SEC;
+ its.it_value.tv_nsec = nsecs % UM_NSEC_PER_SEC;
- if(its_in != NULL) {
- its.it_value.tv_sec = its_in->it_value.tv_sec;
- its.it_value.tv_nsec = its_in->it_value.tv_nsec;
- } else {
- its.it_value.tv_sec = 0;
- its.it_value.tv_nsec = nsec;
- }
+ its.it_interval.tv_sec = nsecs / UM_NSEC_PER_SEC;
+ its.it_interval.tv_nsec = nsecs % UM_NSEC_PER_SEC;
- its.it_interval.tv_sec = 0;
- its.it_interval.tv_nsec = nsec;
-
- if(timer_settime(*t, 0, &its, NULL) == -1) {
+ if (timer_settime(event_high_res_timer, 0, &its, NULL) == -1)
return -errno;
- }
return 0;
}
-/**
- * os_timer_remain() - returns the remaining nano seconds of the given interval
- * timer
- * Because this is the remaining time of an interval timer, which correspondends
- * to HZ, this value can never be bigger than one second. Just
- * the nanosecond part of the timer is returned.
- * The returned time is relative to the start time of the interval timer.
- * Return an negative value in an error case.
- */
-long os_timer_remain(void* timer)
+int os_timer_one_shot(unsigned long long nsecs)
{
- struct itimerspec its;
- timer_t* t = timer;
-
- if(t == NULL) {
- t = &event_high_res_timer;
- }
-
- if(timer_gettime(t, &its) == -1) {
- return -errno;
- }
+ struct itimerspec its = {
+ .it_value.tv_sec = nsecs / UM_NSEC_PER_SEC,
+ .it_value.tv_nsec = nsecs % UM_NSEC_PER_SEC,
- return its.it_value.tv_nsec;
-}
-
-int os_timer_one_shot(int ticks)
-{
- struct itimerspec its;
- unsigned long long nsec;
- unsigned long sec;
-
- nsec = (ticks + 1);
- sec = nsec / UM_NSEC_PER_SEC;
- nsec = nsec % UM_NSEC_PER_SEC;
-
- its.it_value.tv_sec = nsec / UM_NSEC_PER_SEC;
- its.it_value.tv_nsec = nsec;
-
- its.it_interval.tv_sec = 0;
- its.it_interval.tv_nsec = 0; // we cheat here
+ .it_interval.tv_sec = 0,
+ .it_interval.tv_nsec = 0, // we cheat here
+ };
timer_settime(event_high_res_timer, 0, &its, NULL);
return 0;
@@ -135,24 +82,13 @@ int os_timer_one_shot(int ticks)
/**
* os_timer_disable() - disable the posix (interval) timer
- * Returns the remaining interval timer time in nanoseconds
*/
-long long os_timer_disable(void)
+void os_timer_disable(void)
{
struct itimerspec its;
memset(&its, 0, sizeof(struct itimerspec));
- timer_settime(event_high_res_timer, 0, &its, &its);
-
- return its.it_value.tv_sec * UM_NSEC_PER_SEC + its.it_value.tv_nsec;
-}
-
-long long os_vnsecs(void)
-{
- struct timespec ts;
-
- clock_gettime(CLOCK_PROCESS_CPUTIME_ID,&ts);
- return timespec_to_ns(&ts);
+ timer_settime(event_high_res_timer, 0, &its, NULL);
}
long long os_nsecs(void)
@@ -169,21 +105,14 @@ long long os_nsecs(void)
*/
void os_idle_sleep(unsigned long long nsecs)
{
- struct timespec ts;
-
- if (nsecs <= 0) {
- return;
- }
-
- ts = ((struct timespec) {
- .tv_sec = nsecs / UM_NSEC_PER_SEC,
- .tv_nsec = nsecs % UM_NSEC_PER_SEC
- });
+ struct timespec ts = {
+ .tv_sec = nsecs / UM_NSEC_PER_SEC,
+ .tv_nsec = nsecs % UM_NSEC_PER_SEC
+ };
/*
* Relay the signal if clock_nanosleep is interrupted.
*/
- if (clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL)) {
+ if (clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL))
deliver_alarm();
- }
}
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 9df2d1cb7a9e..d0bbca65e4a4 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2733,6 +2733,7 @@ config OLPC
select OF
select OF_PROMTREE
select IRQ_DOMAIN
+ select OLPC_EC
---help---
Add support for detecting the unique features of the OLPC
XO hardware.
diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile
index 39106111be86..34773395139a 100644
--- a/arch/x86/entry/vdso/Makefile
+++ b/arch/x86/entry/vdso/Makefile
@@ -56,8 +56,7 @@ VDSO_LDFLAGS_vdso.lds = -m elf_x86_64 -soname linux-vdso.so.1 --no-undefined \
-z max-page-size=4096
$(obj)/vdso64.so.dbg: $(obj)/vdso.lds $(vobjs) FORCE
- $(call if_changed,vdso)
- $(call if_changed,vdso_check)
+ $(call if_changed,vdso_and_check)
HOST_EXTRACFLAGS += -I$(srctree)/tools/include -I$(srctree)/include/uapi -I$(srctree)/arch/$(SUBARCH)/include/uapi
hostprogs-y += vdso2c
@@ -127,8 +126,7 @@ $(obj)/%.so: $(obj)/%.so.dbg FORCE
$(call if_changed,objcopy)
$(obj)/vdsox32.so.dbg: $(obj)/vdsox32.lds $(vobjx32s) FORCE
- $(call if_changed,vdso)
- $(call if_changed,vdso_check)
+ $(call if_changed,vdso_and_check)
CPPFLAGS_vdso32.lds = $(CPPFLAGS_vdso.lds)
VDSO_LDFLAGS_vdso32.lds = -m elf_i386 -soname linux-gate.so.1
@@ -167,8 +165,7 @@ $(obj)/vdso32.so.dbg: FORCE \
$(obj)/vdso32/note.o \
$(obj)/vdso32/system_call.o \
$(obj)/vdso32/sigreturn.o
- $(call if_changed,vdso)
- $(call if_changed,vdso_check)
+ $(call if_changed,vdso_and_check)
#
# The DSO images are built using a special linker script.
@@ -184,6 +181,9 @@ VDSO_LDFLAGS = -shared $(call ld-option, --hash-style=both) \
-Bsymbolic
GCOV_PROFILE := n
+quiet_cmd_vdso_and_check = VDSO $@
+ cmd_vdso_and_check = $(cmd_vdso); $(cmd_vdso_check)
+
#
# Install the unstripped copies of vdso*.so. If our toolchain supports
# build-id, install .build-id links as well.
diff --git a/arch/x86/events/amd/uncore.c b/arch/x86/events/amd/uncore.c
index 85e6984c560b..a6ea07f2aa84 100644
--- a/arch/x86/events/amd/uncore.c
+++ b/arch/x86/events/amd/uncore.c
@@ -202,15 +202,22 @@ static int amd_uncore_event_init(struct perf_event *event)
hwc->config = event->attr.config & AMD64_RAW_EVENT_MASK_NB;
hwc->idx = -1;
+ if (event->cpu < 0)
+ return -EINVAL;
+
/*
* SliceMask and ThreadMask need to be set for certain L3 events in
* Family 17h. For other events, the two fields do not affect the count.
*/
- if (l3_mask)
- hwc->config |= (AMD64_L3_SLICE_MASK | AMD64_L3_THREAD_MASK);
+ if (l3_mask && is_llc_event(event)) {
+ int thread = 2 * (cpu_data(event->cpu).cpu_core_id % 4);
- if (event->cpu < 0)
- return -EINVAL;
+ if (smp_num_siblings > 1)
+ thread += cpu_data(event->cpu).apicid & 1;
+
+ hwc->config |= (1ULL << (AMD64_L3_THREAD_SHIFT + thread) &
+ AMD64_L3_THREAD_MASK) | AMD64_L3_SLICE_MASK;
+ }
uncore = event_to_amd_uncore(event);
if (!uncore)
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index bda450ff51ee..9e911a96972b 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -2161,12 +2161,10 @@ static void intel_pmu_disable_event(struct perf_event *event)
cpuc->intel_ctrl_host_mask &= ~(1ull << hwc->idx);
cpuc->intel_cp_status &= ~(1ull << hwc->idx);
- if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) {
+ if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL))
intel_pmu_disable_fixed(hwc);
- return;
- }
-
- x86_pmu_disable_event(event);
+ else
+ x86_pmu_disable_event(event);
/*
* Needs to be called after x86_pmu_disable_event,
diff --git a/arch/x86/include/asm/olpc.h b/arch/x86/include/asm/olpc.h
index c2bf1de5d901..6fe76282aceb 100644
--- a/arch/x86/include/asm/olpc.h
+++ b/arch/x86/include/asm/olpc.h
@@ -9,12 +9,10 @@
struct olpc_platform_t {
int flags;
uint32_t boardrev;
- int ecver;
};
#define OLPC_F_PRESENT 0x01
#define OLPC_F_DCON 0x02
-#define OLPC_F_EC_WIDE_SCI 0x04
#ifdef CONFIG_OLPC
@@ -64,13 +62,6 @@ static inline int olpc_board_at_least(uint32_t rev)
return olpc_platform_info.boardrev >= rev;
}
-extern void olpc_ec_wakeup_set(u16 value);
-extern void olpc_ec_wakeup_clear(u16 value);
-extern bool olpc_ec_wakeup_available(void);
-
-extern int olpc_ec_mask_write(u16 bits);
-extern int olpc_ec_sci_query(u16 *sci_value);
-
#else
static inline int machine_is_olpc(void)
@@ -83,14 +74,6 @@ static inline int olpc_has_dcon(void)
return 0;
}
-static inline void olpc_ec_wakeup_set(u16 value) { }
-static inline void olpc_ec_wakeup_clear(u16 value) { }
-
-static inline bool olpc_ec_wakeup_available(void)
-{
- return false;
-}
-
#endif
#ifdef CONFIG_OLPC_XO1_PM
@@ -101,20 +84,6 @@ extern void olpc_xo1_pm_wakeup_clear(u16 value);
extern int pci_olpc_init(void);
-/* SCI source values */
-
-#define EC_SCI_SRC_EMPTY 0x00
-#define EC_SCI_SRC_GAME 0x01
-#define EC_SCI_SRC_BATTERY 0x02
-#define EC_SCI_SRC_BATSOC 0x04
-#define EC_SCI_SRC_BATERR 0x08
-#define EC_SCI_SRC_EBOOK 0x10 /* XO-1 only */
-#define EC_SCI_SRC_WLAN 0x20 /* XO-1 only */
-#define EC_SCI_SRC_ACPWR 0x40
-#define EC_SCI_SRC_BATCRIT 0x80
-#define EC_SCI_SRC_GPWAKE 0x100 /* XO-1.5 only */
-#define EC_SCI_SRC_ALL 0x1FF
-
/* GPIO assignments */
#define OLPC_GPIO_MIC_AC 1
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index 0f01c7b1d217..08bbf648827b 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -1213,13 +1213,9 @@ void __ref arch_remove_memory(int nid, u64 start, u64 size,
{
unsigned long start_pfn = start >> PAGE_SHIFT;
unsigned long nr_pages = size >> PAGE_SHIFT;
- struct page *page = pfn_to_page(start_pfn);
- struct zone *zone;
+ struct page *page = pfn_to_page(start_pfn) + vmem_altmap_offset(altmap);
+ struct zone *zone = page_zone(page);
- /* With altmap the first mapped page is offset from @start */
- if (altmap)
- page += vmem_altmap_offset(altmap);
- zone = page_zone(page);
__remove_pages(zone, start_pfn, nr_pages, altmap);
kernel_physical_mapping_remove(start, start + size);
}
diff --git a/arch/x86/platform/olpc/olpc.c b/arch/x86/platform/olpc/olpc.c
index c85d485eb4f8..ee2beda590d0 100644
--- a/arch/x86/platform/olpc/olpc.c
+++ b/arch/x86/platform/olpc/olpc.c
@@ -26,9 +26,6 @@
struct olpc_platform_t olpc_platform_info;
EXPORT_SYMBOL_GPL(olpc_platform_info);
-/* EC event mask to be applied during suspend (defining wakeup sources). */
-static u16 ec_wakeup_mask;
-
/* what the timeout *should* be (in ms) */
#define EC_BASE_TIMEOUT 20
@@ -182,83 +179,6 @@ err:
return ret;
}
-void olpc_ec_wakeup_set(u16 value)
-{
- ec_wakeup_mask |= value;
-}
-EXPORT_SYMBOL_GPL(olpc_ec_wakeup_set);
-
-void olpc_ec_wakeup_clear(u16 value)
-{
- ec_wakeup_mask &= ~value;
-}
-EXPORT_SYMBOL_GPL(olpc_ec_wakeup_clear);
-
-/*
- * Returns true if the compile and runtime configurations allow for EC events
- * to wake the system.
- */
-bool olpc_ec_wakeup_available(void)
-{
- if (!machine_is_olpc())
- return false;
-
- /*
- * XO-1 EC wakeups are available when olpc-xo1-sci driver is
- * compiled in
- */
-#ifdef CONFIG_OLPC_XO1_SCI
- if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) /* XO-1 */
- return true;
-#endif
-
- /*
- * XO-1.5 EC wakeups are available when olpc-xo15-sci driver is
- * compiled in
- */
-#ifdef CONFIG_OLPC_XO15_SCI
- if (olpc_platform_info.boardrev >= olpc_board_pre(0xd0)) /* XO-1.5 */
- return true;
-#endif
-
- return false;
-}
-EXPORT_SYMBOL_GPL(olpc_ec_wakeup_available);
-
-int olpc_ec_mask_write(u16 bits)
-{
- if (olpc_platform_info.flags & OLPC_F_EC_WIDE_SCI) {
- __be16 ec_word = cpu_to_be16(bits);
- return olpc_ec_cmd(EC_WRITE_EXT_SCI_MASK, (void *) &ec_word, 2,
- NULL, 0);
- } else {
- unsigned char ec_byte = bits & 0xff;
- return olpc_ec_cmd(EC_WRITE_SCI_MASK, &ec_byte, 1, NULL, 0);
- }
-}
-EXPORT_SYMBOL_GPL(olpc_ec_mask_write);
-
-int olpc_ec_sci_query(u16 *sci_value)
-{
- int ret;
-
- if (olpc_platform_info.flags & OLPC_F_EC_WIDE_SCI) {
- __be16 ec_word;
- ret = olpc_ec_cmd(EC_EXT_SCI_QUERY,
- NULL, 0, (void *) &ec_word, 2);
- if (ret == 0)
- *sci_value = be16_to_cpu(ec_word);
- } else {
- unsigned char ec_byte;
- ret = olpc_ec_cmd(EC_SCI_QUERY, NULL, 0, &ec_byte, 1);
- if (ret == 0)
- *sci_value = ec_byte;
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(olpc_ec_sci_query);
-
static bool __init check_ofw_architecture(struct device_node *root)
{
const char *olpc_arch;
@@ -292,6 +212,10 @@ static bool __init platform_detect(void)
if (success) {
olpc_platform_info.boardrev = get_board_revision(root);
olpc_platform_info.flags |= OLPC_F_PRESENT;
+
+ pr_info("OLPC board revision %s%X\n",
+ ((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
+ olpc_platform_info.boardrev >> 4);
}
of_node_put(root);
@@ -311,27 +235,8 @@ static int __init add_xo1_platform_devices(void)
return PTR_ERR_OR_ZERO(pdev);
}
-static int olpc_xo1_ec_probe(struct platform_device *pdev)
-{
- /* get the EC revision */
- olpc_ec_cmd(EC_FIRMWARE_REV, NULL, 0,
- (unsigned char *) &olpc_platform_info.ecver, 1);
-
- /* EC version 0x5f adds support for wide SCI mask */
- if (olpc_platform_info.ecver >= 0x5f)
- olpc_platform_info.flags |= OLPC_F_EC_WIDE_SCI;
-
- pr_info("OLPC board revision %s%X (EC=%x)\n",
- ((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
- olpc_platform_info.boardrev >> 4,
- olpc_platform_info.ecver);
-
- return 0;
-}
static int olpc_xo1_ec_suspend(struct platform_device *pdev)
{
- olpc_ec_mask_write(ec_wakeup_mask);
-
/*
* Squelch SCIs while suspended. This is a fix for
* <http://dev.laptop.org/ticket/1835>.
@@ -355,15 +260,27 @@ static int olpc_xo1_ec_resume(struct platform_device *pdev)
}
static struct olpc_ec_driver ec_xo1_driver = {
- .probe = olpc_xo1_ec_probe,
.suspend = olpc_xo1_ec_suspend,
.resume = olpc_xo1_ec_resume,
.ec_cmd = olpc_xo1_ec_cmd,
+#ifdef CONFIG_OLPC_XO1_SCI
+ /*
+ * XO-1 EC wakeups are available when olpc-xo1-sci driver is
+ * compiled in
+ */
+ .wakeup_available = true,
+#endif
};
static struct olpc_ec_driver ec_xo1_5_driver = {
- .probe = olpc_xo1_ec_probe,
.ec_cmd = olpc_xo1_ec_cmd,
+#ifdef CONFIG_OLPC_XO1_5_SCI
+ /*
+ * XO-1.5 EC wakeups are available when olpc-xo15-sci driver is
+ * compiled in
+ */
+ .wakeup_available = true,
+#endif
};
static int __init olpc_init(void)
diff --git a/arch/x86/platform/olpc/olpc_dt.c b/arch/x86/platform/olpc/olpc_dt.c
index c78bfc16a3ca..26d1f6693789 100644
--- a/arch/x86/platform/olpc/olpc_dt.c
+++ b/arch/x86/platform/olpc/olpc_dt.c
@@ -216,7 +216,7 @@ static u32 __init olpc_dt_get_board_revision(void)
return be32_to_cpu(rev);
}
-int olpc_dt_compatible_match(phandle node, const char *compat)
+static int __init olpc_dt_compatible_match(phandle node, const char *compat)
{
char buf[64], *p;
int plen, len;
diff --git a/drivers/dax/dax-private.h b/drivers/dax/dax-private.h
index b4177aafbbd1..c915889d1769 100644
--- a/drivers/dax/dax-private.h
+++ b/drivers/dax/dax-private.h
@@ -43,8 +43,6 @@ struct dax_region {
* @target_node: effective numa node if dev_dax memory range is onlined
* @dev - device core
* @pgmap - pgmap for memmap setup / lifetime (driver owned)
- * @ref: pgmap reference count (driver owned)
- * @cmp: @ref final put completion (driver owned)
*/
struct dev_dax {
struct dax_region *region;
@@ -52,8 +50,6 @@ struct dev_dax {
int target_node;
struct device dev;
struct dev_pagemap pgmap;
- struct percpu_ref ref;
- struct completion cmp;
};
static inline struct dev_dax *to_dev_dax(struct device *dev)
diff --git a/drivers/dax/device.c b/drivers/dax/device.c
index 8465d12fecba..1af823b2fe6b 100644
--- a/drivers/dax/device.c
+++ b/drivers/dax/device.c
@@ -14,37 +14,6 @@
#include "dax-private.h"
#include "bus.h"
-static struct dev_dax *ref_to_dev_dax(struct percpu_ref *ref)
-{
- return container_of(ref, struct dev_dax, ref);
-}
-
-static void dev_dax_percpu_release(struct percpu_ref *ref)
-{
- struct dev_dax *dev_dax = ref_to_dev_dax(ref);
-
- dev_dbg(&dev_dax->dev, "%s\n", __func__);
- complete(&dev_dax->cmp);
-}
-
-static void dev_dax_percpu_exit(struct percpu_ref *ref)
-{
- struct dev_dax *dev_dax = ref_to_dev_dax(ref);
-
- dev_dbg(&dev_dax->dev, "%s\n", __func__);
- wait_for_completion(&dev_dax->cmp);
- percpu_ref_exit(ref);
-}
-
-static void dev_dax_percpu_kill(struct percpu_ref *data)
-{
- struct percpu_ref *ref = data;
- struct dev_dax *dev_dax = ref_to_dev_dax(ref);
-
- dev_dbg(&dev_dax->dev, "%s\n", __func__);
- percpu_ref_kill(ref);
-}
-
static int check_vma(struct dev_dax *dev_dax, struct vm_area_struct *vma,
const char *func)
{
@@ -459,15 +428,7 @@ int dev_dax_probe(struct device *dev)
return -EBUSY;
}
- init_completion(&dev_dax->cmp);
- rc = percpu_ref_init(&dev_dax->ref, dev_dax_percpu_release, 0,
- GFP_KERNEL);
- if (rc)
- return rc;
-
- dev_dax->pgmap.ref = &dev_dax->ref;
- dev_dax->pgmap.kill = dev_dax_percpu_kill;
- dev_dax->pgmap.cleanup = dev_dax_percpu_exit;
+ dev_dax->pgmap.type = MEMORY_DEVICE_DEVDAX;
addr = devm_memremap_pages(dev, &dev_dax->pgmap);
if (IS_ERR(addr))
return PTR_ERR(addr);
diff --git a/drivers/dax/pmem/core.c b/drivers/dax/pmem/core.c
index f9f51786d556..6eb6dfdf19bf 100644
--- a/drivers/dax/pmem/core.c
+++ b/drivers/dax/pmem/core.c
@@ -16,7 +16,7 @@ struct dev_dax *__dax_pmem_probe(struct device *dev, enum dev_dax_subsys subsys)
struct dev_dax *dev_dax;
struct nd_namespace_io *nsio;
struct dax_region *dax_region;
- struct dev_pagemap pgmap = { 0 };
+ struct dev_pagemap pgmap = { };
struct nd_namespace_common *ndns;
struct nd_dax *nd_dax = to_nd_dax(dev);
struct nd_pfn *nd_pfn = &nd_dax->nd_pfn;
diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig
index dba2613f7180..96b9814e6d06 100644
--- a/drivers/gpu/drm/nouveau/Kconfig
+++ b/drivers/gpu/drm/nouveau/Kconfig
@@ -84,11 +84,11 @@ config DRM_NOUVEAU_BACKLIGHT
config DRM_NOUVEAU_SVM
bool "(EXPERIMENTAL) Enable SVM (Shared Virtual Memory) support"
- depends on ARCH_HAS_HMM
+ depends on DEVICE_PRIVATE
depends on DRM_NOUVEAU
+ depends on HMM_MIRROR
depends on STAGING
- select HMM_MIRROR
- select DEVICE_PRIVATE
+ select MIGRATE_VMA_HELPER
default n
help
Say Y here if you want to enable experimental support for
diff --git a/drivers/gpu/drm/nouveau/nouveau_dmem.c b/drivers/gpu/drm/nouveau/nouveau_dmem.c
index 40c47d6a7d78..42c026010938 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dmem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dmem.c
@@ -72,7 +72,8 @@ struct nouveau_dmem_migrate {
};
struct nouveau_dmem {
- struct hmm_devmem *devmem;
+ struct nouveau_drm *drm;
+ struct dev_pagemap pagemap;
struct nouveau_dmem_migrate migrate;
struct list_head chunk_free;
struct list_head chunk_full;
@@ -80,6 +81,11 @@ struct nouveau_dmem {
struct mutex mutex;
};
+static inline struct nouveau_dmem *page_to_dmem(struct page *page)
+{
+ return container_of(page->pgmap, struct nouveau_dmem, pagemap);
+}
+
struct nouveau_dmem_fault {
struct nouveau_drm *drm;
struct nouveau_fence *fence;
@@ -96,14 +102,10 @@ struct nouveau_migrate {
unsigned long dma_nr;
};
-static void
-nouveau_dmem_free(struct hmm_devmem *devmem, struct page *page)
+static void nouveau_dmem_page_free(struct page *page)
{
- struct nouveau_dmem_chunk *chunk;
- unsigned long idx;
-
- chunk = (void *)hmm_devmem_page_get_drvdata(page);
- idx = page_to_pfn(page) - chunk->pfn_first;
+ struct nouveau_dmem_chunk *chunk = page->zone_device_data;
+ unsigned long idx = page_to_pfn(page) - chunk->pfn_first;
/*
* FIXME:
@@ -148,11 +150,12 @@ nouveau_dmem_fault_alloc_and_copy(struct vm_area_struct *vma,
if (!spage || !(src_pfns[i] & MIGRATE_PFN_MIGRATE))
continue;
- dpage = hmm_vma_alloc_locked_page(vma, addr);
+ dpage = alloc_page_vma(GFP_HIGHUSER, vma, addr);
if (!dpage) {
dst_pfns[i] = MIGRATE_PFN_ERROR;
continue;
}
+ lock_page(dpage);
dst_pfns[i] = migrate_pfn(page_to_pfn(dpage)) |
MIGRATE_PFN_LOCKED;
@@ -194,7 +197,7 @@ nouveau_dmem_fault_alloc_and_copy(struct vm_area_struct *vma,
dst_addr = fault->dma[fault->npages++];
- chunk = (void *)hmm_devmem_page_get_drvdata(spage);
+ chunk = spage->zone_device_data;
src_addr = page_to_pfn(spage) - chunk->pfn_first;
src_addr = (src_addr << PAGE_SHIFT) + chunk->bo->bo.offset;
@@ -259,29 +262,21 @@ static const struct migrate_vma_ops nouveau_dmem_fault_migrate_ops = {
.finalize_and_map = nouveau_dmem_fault_finalize_and_map,
};
-static vm_fault_t
-nouveau_dmem_fault(struct hmm_devmem *devmem,
- struct vm_area_struct *vma,
- unsigned long addr,
- const struct page *page,
- unsigned int flags,
- pmd_t *pmdp)
+static vm_fault_t nouveau_dmem_migrate_to_ram(struct vm_fault *vmf)
{
- struct drm_device *drm_dev = dev_get_drvdata(devmem->device);
+ struct nouveau_dmem *dmem = page_to_dmem(vmf->page);
unsigned long src[1] = {0}, dst[1] = {0};
- struct nouveau_dmem_fault fault = {0};
+ struct nouveau_dmem_fault fault = { .drm = dmem->drm };
int ret;
-
-
/*
* FIXME what we really want is to find some heuristic to migrate more
* than just one page on CPU fault. When such fault happens it is very
* likely that more surrounding page will CPU fault too.
*/
- fault.drm = nouveau_drm(drm_dev);
- ret = migrate_vma(&nouveau_dmem_fault_migrate_ops, vma, addr,
- addr + PAGE_SIZE, src, dst, &fault);
+ ret = migrate_vma(&nouveau_dmem_fault_migrate_ops, vmf->vma,
+ vmf->address, vmf->address + PAGE_SIZE,
+ src, dst, &fault);
if (ret)
return VM_FAULT_SIGBUS;
@@ -291,10 +286,9 @@ nouveau_dmem_fault(struct hmm_devmem *devmem,
return 0;
}
-static const struct hmm_devmem_ops
-nouveau_dmem_devmem_ops = {
- .free = nouveau_dmem_free,
- .fault = nouveau_dmem_fault,
+static const struct dev_pagemap_ops nouveau_dmem_pagemap_ops = {
+ .page_free = nouveau_dmem_page_free,
+ .migrate_to_ram = nouveau_dmem_migrate_to_ram,
};
static int
@@ -580,7 +574,8 @@ void
nouveau_dmem_init(struct nouveau_drm *drm)
{
struct device *device = drm->dev->dev;
- unsigned long i, size;
+ struct resource *res;
+ unsigned long i, size, pfn_first;
int ret;
/* This only make sense on PASCAL or newer */
@@ -590,6 +585,7 @@ nouveau_dmem_init(struct nouveau_drm *drm)
if (!(drm->dmem = kzalloc(sizeof(*drm->dmem), GFP_KERNEL)))
return;
+ drm->dmem->drm = drm;
mutex_init(&drm->dmem->mutex);
INIT_LIST_HEAD(&drm->dmem->chunk_free);
INIT_LIST_HEAD(&drm->dmem->chunk_full);
@@ -599,11 +595,8 @@ nouveau_dmem_init(struct nouveau_drm *drm)
/* Initialize migration dma helpers before registering memory */
ret = nouveau_dmem_migrate_init(drm);
- if (ret) {
- kfree(drm->dmem);
- drm->dmem = NULL;
- return;
- }
+ if (ret)
+ goto out_free;
/*
* FIXME we need some kind of policy to decide how much VRAM we
@@ -611,14 +604,16 @@ nouveau_dmem_init(struct nouveau_drm *drm)
* and latter if we want to do thing like over commit then we
* could revisit this.
*/
- drm->dmem->devmem = hmm_devmem_add(&nouveau_dmem_devmem_ops,
- device, size);
- if (IS_ERR(drm->dmem->devmem)) {
- kfree(drm->dmem);
- drm->dmem = NULL;
- return;
- }
-
+ res = devm_request_free_mem_region(device, &iomem_resource, size);
+ if (IS_ERR(res))
+ goto out_free;
+ drm->dmem->pagemap.type = MEMORY_DEVICE_PRIVATE;
+ drm->dmem->pagemap.res = *res;
+ drm->dmem->pagemap.ops = &nouveau_dmem_pagemap_ops;
+ if (IS_ERR(devm_memremap_pages(device, &drm->dmem->pagemap)))
+ goto out_free;
+
+ pfn_first = res->start >> PAGE_SHIFT;
for (i = 0; i < (size / DMEM_CHUNK_SIZE); ++i) {
struct nouveau_dmem_chunk *chunk;
struct page *page;
@@ -631,17 +626,19 @@ nouveau_dmem_init(struct nouveau_drm *drm)
}
chunk->drm = drm;
- chunk->pfn_first = drm->dmem->devmem->pfn_first;
- chunk->pfn_first += (i * DMEM_CHUNK_NPAGES);
+ chunk->pfn_first = pfn_first + (i * DMEM_CHUNK_NPAGES);
list_add_tail(&chunk->list, &drm->dmem->chunk_empty);
page = pfn_to_page(chunk->pfn_first);
- for (j = 0; j < DMEM_CHUNK_NPAGES; ++j, ++page) {
- hmm_devmem_page_set_drvdata(page, (long)chunk);
- }
+ for (j = 0; j < DMEM_CHUNK_NPAGES; ++j, ++page)
+ page->zone_device_data = chunk;
}
NV_INFO(drm, "DMEM: registered %ldMB of device memory\n", size >> 20);
+ return;
+out_free:
+ kfree(drm->dmem);
+ drm->dmem = NULL;
}
static void
@@ -697,7 +694,7 @@ nouveau_dmem_migrate_alloc_and_copy(struct vm_area_struct *vma,
if (!dpage || dst_pfns[i] == MIGRATE_PFN_ERROR)
continue;
- chunk = (void *)hmm_devmem_page_get_drvdata(dpage);
+ chunk = dpage->zone_device_data;
dst_addr = page_to_pfn(dpage) - chunk->pfn_first;
dst_addr = (dst_addr << PAGE_SHIFT) + chunk->bo->bo.offset;
@@ -832,13 +829,7 @@ out:
static inline bool
nouveau_dmem_page(struct nouveau_drm *drm, struct page *page)
{
- if (!is_device_private_page(page))
- return false;
-
- if (drm->dmem->devmem != page->pgmap->data)
- return false;
-
- return true;
+ return is_device_private_page(page) && drm->dmem == page_to_dmem(page);
}
void
@@ -867,7 +858,7 @@ nouveau_dmem_convert_pfn(struct nouveau_drm *drm,
continue;
}
- chunk = (void *)hmm_devmem_page_get_drvdata(page);
+ chunk = page->zone_device_data;
addr = page_to_pfn(page) - chunk->pfn_first;
addr = (addr + chunk->bo->bo.mem.start) << PAGE_SHIFT;
diff --git a/drivers/gpu/drm/nouveau/nouveau_svm.c b/drivers/gpu/drm/nouveau/nouveau_svm.c
index 93ed43c413f0..8c92374afcf2 100644
--- a/drivers/gpu/drm/nouveau/nouveau_svm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_svm.c
@@ -649,7 +649,7 @@ nouveau_svm_fault(struct nvif_notify *notify)
range.values = nouveau_svm_pfn_values;
range.pfn_shift = NVIF_VMM_PFNMAP_V0_ADDR_SHIFT;
again:
- ret = hmm_vma_fault(&range, true);
+ ret = hmm_vma_fault(&svmm->mirror, &range, true);
if (ret == 0) {
mutex_lock(&svmm->mutex);
if (!hmm_vma_range_done(&range)) {
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 5b37c570f611..8063b1d567b1 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -393,7 +393,7 @@ static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev)
if (!IS_ENABLED(CONFIG_ASUS_WMI))
return false;
- ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS2,
+ ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS,
ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, &value);
hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value);
if (ret)
diff --git a/drivers/hid/hid-sensor-custom.c b/drivers/hid/hid-sensor-custom.c
index c60f82673cf2..fb827c295842 100644
--- a/drivers/hid/hid-sensor-custom.c
+++ b/drivers/hid/hid-sensor-custom.c
@@ -687,7 +687,7 @@ static int hid_sensor_custom_open(struct inode *inode, struct file *file)
if (test_and_set_bit(0, &sensor_inst->misc_opened))
return -EBUSY;
- return nonseekable_open(inode, file);
+ return stream_open(inode, file);
}
static __poll_t hid_sensor_custom_poll(struct file *file,
diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c
index 3b73e0f17848..505c562a5daa 100644
--- a/drivers/input/mousedev.c
+++ b/drivers/input/mousedev.c
@@ -558,7 +558,7 @@ static int mousedev_open(struct inode *inode, struct file *file)
goto err_free_client;
file->private_data = client;
- nonseekable_open(inode, file);
+ stream_open(inode, file);
return 0;
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index b709481a8de6..ab4eb750bbdd 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -54,7 +54,7 @@ config ARMADA_37XX_RWTM_MBOX
config OMAP2PLUS_MBOX
tristate "OMAP2+ Mailbox framework support"
- depends on ARCH_OMAP2PLUS
+ depends on ARCH_OMAP2PLUS || ARCH_K3
help
Mailbox implementation for OMAP family chips with hardware for
interprocessor communication involving DSP, IVA1.0 and IVA2 in
diff --git a/drivers/mailbox/arm_mhu.c b/drivers/mailbox/arm_mhu.c
index b47851856086..9da236552bd7 100644
--- a/drivers/mailbox/arm_mhu.c
+++ b/drivers/mailbox/arm_mhu.c
@@ -5,16 +5,13 @@
* Author: Jassi Brar <jaswinder.singh@linaro.org>
*/
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#include <linux/mutex.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
+#include <linux/amba/bus.h>
+#include <linux/device.h>
#include <linux/err.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/amba/bus.h>
#include <linux/mailbox_controller.h>
+#include <linux/module.h>
#define INTR_STAT_OFS 0x0
#define INTR_SET_OFS 0x8
diff --git a/drivers/mailbox/bcm-flexrm-mailbox.c b/drivers/mailbox/bcm-flexrm-mailbox.c
index 43b336316fde..8ee9db274802 100644
--- a/drivers/mailbox/bcm-flexrm-mailbox.c
+++ b/drivers/mailbox/bcm-flexrm-mailbox.c
@@ -1163,8 +1163,7 @@ static int flexrm_process_completions(struct flexrm_ring *ring)
static int flexrm_debugfs_conf_show(struct seq_file *file, void *offset)
{
- struct platform_device *pdev = to_platform_device(file->private);
- struct flexrm_mbox *mbox = platform_get_drvdata(pdev);
+ struct flexrm_mbox *mbox = dev_get_drvdata(file->private);
/* Write config in file */
flexrm_write_config_in_seqfile(mbox, file);
@@ -1174,8 +1173,7 @@ static int flexrm_debugfs_conf_show(struct seq_file *file, void *offset)
static int flexrm_debugfs_stats_show(struct seq_file *file, void *offset)
{
- struct platform_device *pdev = to_platform_device(file->private);
- struct flexrm_mbox *mbox = platform_get_drvdata(pdev);
+ struct flexrm_mbox *mbox = dev_get_drvdata(file->private);
/* Write stats in file */
flexrm_write_stats_in_seqfile(mbox, file);
diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c
index 25be8bb5e371..9f74dee1a58c 100644
--- a/drivers/mailbox/imx-mailbox.c
+++ b/drivers/mailbox/imx-mailbox.c
@@ -217,8 +217,8 @@ static void imx_mu_shutdown(struct mbox_chan *chan)
if (cp->type == IMX_MU_TYPE_TXDB)
tasklet_kill(&cp->txdb_tasklet);
- imx_mu_xcr_rmw(priv, 0,
- IMX_MU_xCR_TIEn(cp->idx) | IMX_MU_xCR_RIEn(cp->idx));
+ imx_mu_xcr_rmw(priv, 0, IMX_MU_xCR_TIEn(cp->idx) |
+ IMX_MU_xCR_RIEn(cp->idx) | IMX_MU_xCR_GIEn(cp->idx));
free_irq(priv->irq, chan);
}
diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
index f4b1950d35f3..0b821a5b2db8 100644
--- a/drivers/mailbox/mailbox.c
+++ b/drivers/mailbox/mailbox.c
@@ -418,11 +418,13 @@ struct mbox_chan *mbox_request_channel_byname(struct mbox_client *cl,
of_property_for_each_string(np, "mbox-names", prop, mbox_name) {
if (!strncmp(name, mbox_name, strlen(name)))
- break;
+ return mbox_request_channel(cl, index);
index++;
}
- return mbox_request_channel(cl, index);
+ dev_err(cl->dev, "%s() could not locate channel named \"%s\"\n",
+ __func__, name);
+ return ERR_PTR(-EINVAL);
}
EXPORT_SYMBOL_GPL(mbox_request_channel_byname);
diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c
index ca50177a33f2..a3cd63583cf7 100644
--- a/drivers/mailbox/omap-mailbox.c
+++ b/drivers/mailbox/omap-mailbox.c
@@ -3,7 +3,7 @@
* OMAP mailbox driver
*
* Copyright (C) 2006-2009 Nokia Corporation. All rights reserved.
- * Copyright (C) 2013-2016 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2013-2019 Texas Instruments Incorporated - http://www.ti.com
*
* Contact: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
* Suman Anna <s-anna@ti.com>
@@ -141,14 +141,14 @@ void mbox_write_reg(struct omap_mbox_device *mdev, u32 val, size_t ofs)
}
/* Mailbox FIFO handle functions */
-static mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox)
+static u32 mbox_fifo_read(struct omap_mbox *mbox)
{
struct omap_mbox_fifo *fifo = &mbox->rx_fifo;
- return (mbox_msg_t)mbox_read_reg(mbox->parent, fifo->msg);
+ return mbox_read_reg(mbox->parent, fifo->msg);
}
-static void mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg)
+static void mbox_fifo_write(struct omap_mbox *mbox, u32 msg)
{
struct omap_mbox_fifo *fifo = &mbox->tx_fifo;
@@ -256,14 +256,16 @@ static void mbox_rx_work(struct work_struct *work)
{
struct omap_mbox_queue *mq =
container_of(work, struct omap_mbox_queue, work);
- mbox_msg_t msg;
+ mbox_msg_t data;
+ u32 msg;
int len;
while (kfifo_len(&mq->fifo) >= sizeof(msg)) {
len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
WARN_ON(len != sizeof(msg));
+ data = msg;
- mbox_chan_received_data(mq->mbox->chan, (void *)msg);
+ mbox_chan_received_data(mq->mbox->chan, (void *)data);
spin_lock_irq(&mq->lock);
if (mq->full) {
mq->full = false;
@@ -286,7 +288,7 @@ static void __mbox_tx_interrupt(struct omap_mbox *mbox)
static void __mbox_rx_interrupt(struct omap_mbox *mbox)
{
struct omap_mbox_queue *mq = mbox->rxq;
- mbox_msg_t msg;
+ u32 msg;
int len;
while (!mbox_fifo_empty(mbox)) {
@@ -540,13 +542,13 @@ static void omap_mbox_chan_shutdown(struct mbox_chan *chan)
mutex_unlock(&mdev->cfg_lock);
}
-static int omap_mbox_chan_send_noirq(struct omap_mbox *mbox, void *data)
+static int omap_mbox_chan_send_noirq(struct omap_mbox *mbox, u32 msg)
{
int ret = -EBUSY;
if (!mbox_fifo_full(mbox)) {
_omap_mbox_enable_irq(mbox, IRQ_RX);
- mbox_fifo_write(mbox, (mbox_msg_t)data);
+ mbox_fifo_write(mbox, msg);
ret = 0;
_omap_mbox_disable_irq(mbox, IRQ_RX);
@@ -558,12 +560,12 @@ static int omap_mbox_chan_send_noirq(struct omap_mbox *mbox, void *data)
return ret;
}
-static int omap_mbox_chan_send(struct omap_mbox *mbox, void *data)
+static int omap_mbox_chan_send(struct omap_mbox *mbox, u32 msg)
{
int ret = -EBUSY;
if (!mbox_fifo_full(mbox)) {
- mbox_fifo_write(mbox, (mbox_msg_t)data);
+ mbox_fifo_write(mbox, msg);
ret = 0;
}
@@ -576,14 +578,15 @@ static int omap_mbox_chan_send_data(struct mbox_chan *chan, void *data)
{
struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan);
int ret;
+ u32 msg = omap_mbox_message(data);
if (!mbox)
return -EINVAL;
if (mbox->send_no_irq)
- ret = omap_mbox_chan_send_noirq(mbox, data);
+ ret = omap_mbox_chan_send_noirq(mbox, msg);
else
- ret = omap_mbox_chan_send(mbox, data);
+ ret = omap_mbox_chan_send(mbox, msg);
return ret;
}
@@ -657,6 +660,10 @@ static const struct of_device_id omap_mailbox_of_match[] = {
.data = &omap4_data,
},
{
+ .compatible = "ti,am654-mailbox",
+ .data = &omap4_data,
+ },
+ {
/* end */
},
};
@@ -830,7 +837,10 @@ static int omap_mbox_probe(struct platform_device *pdev)
mdev->intr_type = intr_type;
mdev->mboxes = list;
- /* OMAP does not have a Tx-Done IRQ, but rather a Tx-Ready IRQ */
+ /*
+ * OMAP/K3 Mailbox IP does not have a Tx-Done IRQ, but rather a Tx-Ready
+ * IRQ and is needed to run the Tx state machine
+ */
mdev->controller.txdone_irq = true;
mdev->controller.dev = mdev->dev;
mdev->controller.ops = &omap_mbox_chan_ops;
@@ -899,9 +909,8 @@ static int __init omap_mbox_init(void)
return err;
/* kfifo size sanity check: alignment and minimal size */
- mbox_kfifo_size = ALIGN(mbox_kfifo_size, sizeof(mbox_msg_t));
- mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size,
- sizeof(mbox_msg_t));
+ mbox_kfifo_size = ALIGN(mbox_kfifo_size, sizeof(u32));
+ mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size, sizeof(u32));
err = platform_driver_register(&omap_mbox_driver);
if (err)
diff --git a/drivers/mailbox/stm32-ipcc.c b/drivers/mailbox/stm32-ipcc.c
index f91dfb1327c7..5c2d1e1f988b 100644
--- a/drivers/mailbox/stm32-ipcc.c
+++ b/drivers/mailbox/stm32-ipcc.c
@@ -50,6 +50,7 @@ struct stm32_ipcc {
void __iomem *reg_base;
void __iomem *reg_proc;
struct clk *clk;
+ spinlock_t lock; /* protect access to IPCC registers */
int irqs[IPCC_IRQ_NUM];
int wkp;
u32 proc_id;
@@ -58,14 +59,24 @@ struct stm32_ipcc {
u32 xmr;
};
-static inline void stm32_ipcc_set_bits(void __iomem *reg, u32 mask)
+static inline void stm32_ipcc_set_bits(spinlock_t *lock, void __iomem *reg,
+ u32 mask)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(lock, flags);
writel_relaxed(readl_relaxed(reg) | mask, reg);
+ spin_unlock_irqrestore(lock, flags);
}
-static inline void stm32_ipcc_clr_bits(void __iomem *reg, u32 mask)
+static inline void stm32_ipcc_clr_bits(spinlock_t *lock, void __iomem *reg,
+ u32 mask)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(lock, flags);
writel_relaxed(readl_relaxed(reg) & ~mask, reg);
+ spin_unlock_irqrestore(lock, flags);
}
static irqreturn_t stm32_ipcc_rx_irq(int irq, void *data)
@@ -92,7 +103,7 @@ static irqreturn_t stm32_ipcc_rx_irq(int irq, void *data)
mbox_chan_received_data(&ipcc->controller.chans[chan], NULL);
- stm32_ipcc_set_bits(ipcc->reg_proc + IPCC_XSCR,
+ stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XSCR,
RX_BIT_CHAN(chan));
ret = IRQ_HANDLED;
@@ -121,7 +132,7 @@ static irqreturn_t stm32_ipcc_tx_irq(int irq, void *data)
dev_dbg(dev, "%s: chan:%d tx\n", __func__, chan);
/* mask 'tx channel free' interrupt */
- stm32_ipcc_set_bits(ipcc->reg_proc + IPCC_XMR,
+ stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR,
TX_BIT_CHAN(chan));
mbox_chan_txdone(&ipcc->controller.chans[chan], 0);
@@ -141,10 +152,12 @@ static int stm32_ipcc_send_data(struct mbox_chan *link, void *data)
dev_dbg(ipcc->controller.dev, "%s: chan:%d\n", __func__, chan);
/* set channel n occupied */
- stm32_ipcc_set_bits(ipcc->reg_proc + IPCC_XSCR, TX_BIT_CHAN(chan));
+ stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XSCR,
+ TX_BIT_CHAN(chan));
/* unmask 'tx channel free' interrupt */
- stm32_ipcc_clr_bits(ipcc->reg_proc + IPCC_XMR, TX_BIT_CHAN(chan));
+ stm32_ipcc_clr_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR,
+ TX_BIT_CHAN(chan));
return 0;
}
@@ -163,7 +176,8 @@ static int stm32_ipcc_startup(struct mbox_chan *link)
}
/* unmask 'rx channel occupied' interrupt */
- stm32_ipcc_clr_bits(ipcc->reg_proc + IPCC_XMR, RX_BIT_CHAN(chan));
+ stm32_ipcc_clr_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR,
+ RX_BIT_CHAN(chan));
return 0;
}
@@ -175,7 +189,7 @@ static void stm32_ipcc_shutdown(struct mbox_chan *link)
controller);
/* mask rx/tx interrupt */
- stm32_ipcc_set_bits(ipcc->reg_proc + IPCC_XMR,
+ stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR,
RX_BIT_CHAN(chan) | TX_BIT_CHAN(chan));
clk_disable_unprepare(ipcc->clk);
@@ -208,6 +222,8 @@ static int stm32_ipcc_probe(struct platform_device *pdev)
if (!ipcc)
return -ENOMEM;
+ spin_lock_init(&ipcc->lock);
+
/* proc_id */
if (of_property_read_u32(np, "st,proc-id", &ipcc->proc_id)) {
dev_err(dev, "Missing st,proc-id\n");
@@ -259,9 +275,10 @@ static int stm32_ipcc_probe(struct platform_device *pdev)
}
/* mask and enable rx/tx irq */
- stm32_ipcc_set_bits(ipcc->reg_proc + IPCC_XMR,
+ stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR,
RX_BIT_MASK | TX_BIT_MASK);
- stm32_ipcc_set_bits(ipcc->reg_proc + IPCC_XCR, XCR_RXOIE | XCR_TXOIE);
+ stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XCR,
+ XCR_RXOIE | XCR_TXOIE);
/* wakeup */
if (of_property_read_bool(np, "wakeup-source")) {
diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c
index 91f1a0c62779..4c5ba35d48d4 100644
--- a/drivers/mailbox/tegra-hsp.c
+++ b/drivers/mailbox/tegra-hsp.c
@@ -775,18 +775,28 @@ static int __maybe_unused tegra_hsp_resume(struct device *dev)
{
struct tegra_hsp *hsp = dev_get_drvdata(dev);
unsigned int i;
+ struct tegra_hsp_doorbell *db;
- for (i = 0; i < hsp->num_sm; i++) {
- struct tegra_hsp_mailbox *mb = &hsp->mailboxes[i];
+ list_for_each_entry(db, &hsp->doorbells, list) {
+ if (db && db->channel.chan)
+ tegra_hsp_doorbell_startup(db->channel.chan);
+ }
+
+ if (hsp->mailboxes) {
+ for (i = 0; i < hsp->num_sm; i++) {
+ struct tegra_hsp_mailbox *mb = &hsp->mailboxes[i];
- if (mb->channel.chan->cl)
- tegra_hsp_mailbox_startup(mb->channel.chan);
+ if (mb->channel.chan->cl)
+ tegra_hsp_mailbox_startup(mb->channel.chan);
+ }
}
return 0;
}
-static SIMPLE_DEV_PM_OPS(tegra_hsp_pm_ops, NULL, tegra_hsp_resume);
+static const struct dev_pm_ops tegra_hsp_pm_ops = {
+ .resume_noirq = tegra_hsp_resume,
+};
static const struct tegra_hsp_db_map tegra186_hsp_db_map[] = {
{ "ccplex", TEGRA_HSP_DB_MASTER_CCPLEX, HSP_DB_CCPLEX, },
diff --git a/drivers/md/md.c b/drivers/md/md.c
index a114b05e3db4..24638ccedce4 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -5316,7 +5316,8 @@ int mddev_init_writes_pending(struct mddev *mddev)
{
if (mddev->writes_pending.percpu_count_ptr)
return 0;
- if (percpu_ref_init(&mddev->writes_pending, no_op, 0, GFP_KERNEL) < 0)
+ if (percpu_ref_init(&mddev->writes_pending, no_op,
+ PERCPU_REF_ALLOW_REINIT, GFP_KERNEL) < 0)
return -ENOMEM;
/* We want to start with the refcount at zero */
percpu_ref_put(&mddev->writes_pending);
diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c
index 0f81fc56bbfd..55fb6b7433ed 100644
--- a/drivers/nvdimm/pfn_devs.c
+++ b/drivers/nvdimm/pfn_devs.c
@@ -622,7 +622,6 @@ static int __nvdimm_setup_pfn(struct nd_pfn *nd_pfn, struct dev_pagemap *pgmap)
if (offset < reserve)
return -EINVAL;
nd_pfn->npfns = le64_to_cpu(pfn_sb->npfns);
- pgmap->altmap_valid = false;
} else if (nd_pfn->mode == PFN_MODE_PMEM) {
nd_pfn->npfns = PFN_SECTION_ALIGN_UP((resource_size(res)
- offset) / PAGE_SIZE);
@@ -634,7 +633,7 @@ static int __nvdimm_setup_pfn(struct nd_pfn *nd_pfn, struct dev_pagemap *pgmap)
memcpy(altmap, &__altmap, sizeof(*altmap));
altmap->free = PHYS_PFN(offset - reserve);
altmap->alloc = 0;
- pgmap->altmap_valid = true;
+ pgmap->flags |= PGMAP_ALTMAP_VALID;
} else
return -ENXIO;
diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index 24d7fe7c74ed..e7d8cc9f41e8 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -303,24 +303,24 @@ static const struct attribute_group *pmem_attribute_groups[] = {
NULL,
};
-static void __pmem_release_queue(struct percpu_ref *ref)
+static void pmem_pagemap_cleanup(struct dev_pagemap *pgmap)
{
- struct request_queue *q;
+ struct request_queue *q =
+ container_of(pgmap->ref, struct request_queue, q_usage_counter);
- q = container_of(ref, typeof(*q), q_usage_counter);
blk_cleanup_queue(q);
}
-static void pmem_release_queue(void *ref)
+static void pmem_release_queue(void *pgmap)
{
- __pmem_release_queue(ref);
+ pmem_pagemap_cleanup(pgmap);
}
-static void pmem_freeze_queue(struct percpu_ref *ref)
+static void pmem_pagemap_kill(struct dev_pagemap *pgmap)
{
- struct request_queue *q;
+ struct request_queue *q =
+ container_of(pgmap->ref, struct request_queue, q_usage_counter);
- q = container_of(ref, typeof(*q), q_usage_counter);
blk_freeze_queue_start(q);
}
@@ -334,26 +334,16 @@ static void pmem_release_disk(void *__pmem)
put_disk(pmem->disk);
}
-static void pmem_release_pgmap_ops(void *__pgmap)
-{
- dev_pagemap_put_ops();
-}
-
-static void fsdax_pagefree(struct page *page, void *data)
+static void pmem_pagemap_page_free(struct page *page)
{
wake_up_var(&page->_refcount);
}
-static int setup_pagemap_fsdax(struct device *dev, struct dev_pagemap *pgmap)
-{
- dev_pagemap_get_ops();
- if (devm_add_action_or_reset(dev, pmem_release_pgmap_ops, pgmap))
- return -ENOMEM;
- pgmap->type = MEMORY_DEVICE_FS_DAX;
- pgmap->page_free = fsdax_pagefree;
-
- return 0;
-}
+static const struct dev_pagemap_ops fsdax_pagemap_ops = {
+ .page_free = pmem_pagemap_page_free,
+ .kill = pmem_pagemap_kill,
+ .cleanup = pmem_pagemap_cleanup,
+};
static int pmem_attach_disk(struct device *dev,
struct nd_namespace_common *ndns)
@@ -409,11 +399,9 @@ static int pmem_attach_disk(struct device *dev,
pmem->pfn_flags = PFN_DEV;
pmem->pgmap.ref = &q->q_usage_counter;
- pmem->pgmap.kill = pmem_freeze_queue;
- pmem->pgmap.cleanup = __pmem_release_queue;
if (is_nd_pfn(dev)) {
- if (setup_pagemap_fsdax(dev, &pmem->pgmap))
- return -ENOMEM;
+ pmem->pgmap.type = MEMORY_DEVICE_FS_DAX;
+ pmem->pgmap.ops = &fsdax_pagemap_ops;
addr = devm_memremap_pages(dev, &pmem->pgmap);
pfn_sb = nd_pfn->pfn_sb;
pmem->data_offset = le64_to_cpu(pfn_sb->dataoff);
@@ -424,15 +412,14 @@ static int pmem_attach_disk(struct device *dev,
bb_res.start += pmem->data_offset;
} else if (pmem_should_map_pages(dev)) {
memcpy(&pmem->pgmap.res, &nsio->res, sizeof(pmem->pgmap.res));
- pmem->pgmap.altmap_valid = false;
- if (setup_pagemap_fsdax(dev, &pmem->pgmap))
- return -ENOMEM;
+ pmem->pgmap.type = MEMORY_DEVICE_FS_DAX;
+ pmem->pgmap.ops = &fsdax_pagemap_ops;
addr = devm_memremap_pages(dev, &pmem->pgmap);
pmem->pfn_flags |= PFN_MAP;
memcpy(&bb_res, &pmem->pgmap.res, sizeof(bb_res));
} else {
if (devm_add_action_or_reset(dev, pmem_release_queue,
- &q->q_usage_counter))
+ &pmem->pgmap))
return -ENOMEM;
addr = devm_memremap(dev, pmem->phys_addr,
pmem->size, ARCH_MEMREMAP_PMEM);
diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c
index a4994aa3acc0..a3073ce16520 100644
--- a/drivers/pci/p2pdma.c
+++ b/drivers/pci/p2pdma.c
@@ -25,12 +25,6 @@ struct pci_p2pdma {
bool p2pmem_published;
};
-struct p2pdma_pagemap {
- struct dev_pagemap pgmap;
- struct percpu_ref ref;
- struct completion ref_done;
-};
-
static ssize_t size_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -79,31 +73,6 @@ static const struct attribute_group p2pmem_group = {
.name = "p2pmem",
};
-static struct p2pdma_pagemap *to_p2p_pgmap(struct percpu_ref *ref)
-{
- return container_of(ref, struct p2pdma_pagemap, ref);
-}
-
-static void pci_p2pdma_percpu_release(struct percpu_ref *ref)
-{
- struct p2pdma_pagemap *p2p_pgmap = to_p2p_pgmap(ref);
-
- complete(&p2p_pgmap->ref_done);
-}
-
-static void pci_p2pdma_percpu_kill(struct percpu_ref *ref)
-{
- percpu_ref_kill(ref);
-}
-
-static void pci_p2pdma_percpu_cleanup(struct percpu_ref *ref)
-{
- struct p2pdma_pagemap *p2p_pgmap = to_p2p_pgmap(ref);
-
- wait_for_completion(&p2p_pgmap->ref_done);
- percpu_ref_exit(&p2p_pgmap->ref);
-}
-
static void pci_p2pdma_release(void *data)
{
struct pci_dev *pdev = data;
@@ -166,7 +135,6 @@ out:
int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
u64 offset)
{
- struct p2pdma_pagemap *p2p_pgmap;
struct dev_pagemap *pgmap;
void *addr;
int error;
@@ -189,27 +157,15 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
return error;
}
- p2p_pgmap = devm_kzalloc(&pdev->dev, sizeof(*p2p_pgmap), GFP_KERNEL);
- if (!p2p_pgmap)
+ pgmap = devm_kzalloc(&pdev->dev, sizeof(*pgmap), GFP_KERNEL);
+ if (!pgmap)
return -ENOMEM;
-
- init_completion(&p2p_pgmap->ref_done);
- error = percpu_ref_init(&p2p_pgmap->ref,
- pci_p2pdma_percpu_release, 0, GFP_KERNEL);
- if (error)
- goto pgmap_free;
-
- pgmap = &p2p_pgmap->pgmap;
-
pgmap->res.start = pci_resource_start(pdev, bar) + offset;
pgmap->res.end = pgmap->res.start + size - 1;
pgmap->res.flags = pci_resource_flags(pdev, bar);
- pgmap->ref = &p2p_pgmap->ref;
pgmap->type = MEMORY_DEVICE_PCI_P2PDMA;
pgmap->pci_p2pdma_bus_offset = pci_bus_address(pdev, bar) -
pci_resource_start(pdev, bar);
- pgmap->kill = pci_p2pdma_percpu_kill;
- pgmap->cleanup = pci_p2pdma_percpu_cleanup;
addr = devm_memremap_pages(&pdev->dev, pgmap);
if (IS_ERR(addr)) {
@@ -220,7 +176,7 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
error = gen_pool_add_owner(pdev->p2pdma->pool, (unsigned long)addr,
pci_bus_address(pdev, bar) + offset,
resource_size(&pgmap->res), dev_to_node(&pdev->dev),
- &p2p_pgmap->ref);
+ pgmap->ref);
if (error)
goto pages_free;
@@ -232,7 +188,7 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
pages_free:
devm_memunmap_pages(&pdev->dev, pgmap);
pgmap_free:
- devm_kfree(&pdev->dev, p2p_pgmap);
+ devm_kfree(&pdev->dev, pgmap);
return error;
}
EXPORT_SYMBOL_GPL(pci_p2pdma_add_resource);
diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
index 0b3a906b3bf5..971426bb4302 100644
--- a/drivers/platform/Kconfig
+++ b/drivers/platform/Kconfig
@@ -11,3 +11,5 @@ source "drivers/platform/goldfish/Kconfig"
source "drivers/platform/chrome/Kconfig"
source "drivers/platform/mellanox/Kconfig"
+
+source "drivers/platform/olpc/Kconfig"
diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
index 4b2ce58bcd9c..6fda58c021ca 100644
--- a/drivers/platform/Makefile
+++ b/drivers/platform/Makefile
@@ -6,6 +6,6 @@
obj-$(CONFIG_X86) += x86/
obj-$(CONFIG_MELLANOX_PLATFORM) += mellanox/
obj-$(CONFIG_MIPS) += mips/
-obj-$(CONFIG_OLPC) += olpc/
+obj-$(CONFIG_OLPC_EC) += olpc/
obj-$(CONFIG_GOLDFISH) += goldfish/
obj-$(CONFIG_CHROME_PLATFORMS) += chrome/
diff --git a/drivers/platform/olpc/Kconfig b/drivers/platform/olpc/Kconfig
new file mode 100644
index 000000000000..919b489e24e8
--- /dev/null
+++ b/drivers/platform/olpc/Kconfig
@@ -0,0 +1,29 @@
+config OLPC_EC
+ select REGULATOR
+ bool
+
+menuconfig OLPC_XO175
+ bool "Platform support for OLPC XO 1.75 hardware"
+ depends on ARCH_MMP || COMPILE_TEST
+ help
+ Say Y here to get to see options for the ARM-based OLPC platform.
+ This option alone does not add any kernel code.
+
+ Unless you have an OLPC XO laptop, you will want to say N.
+
+if OLPC_XO175
+
+config OLPC_XO175_EC
+ tristate "OLPC XO 1.75 Embedded Controller"
+ depends on SPI_SLAVE
+ depends on INPUT
+ depends on POWER_SUPPLY
+ select OLPC_EC
+ help
+ Include support for the OLPC XO Embedded Controller (EC). The EC
+ provides various platform services, including support for the power,
+ button, restart, shutdown and battery charging status.
+
+ Unless you have an OLPC XO laptop, you will want to say N.
+
+endif # OLPC_XO175
diff --git a/drivers/platform/olpc/Makefile b/drivers/platform/olpc/Makefile
index cee085c6532d..e9b67000cbcb 100644
--- a/drivers/platform/olpc/Makefile
+++ b/drivers/platform/olpc/Makefile
@@ -2,4 +2,5 @@
#
# OLPC XO platform-specific drivers
#
-obj-$(CONFIG_OLPC) += olpc-ec.o
+obj-$(CONFIG_OLPC_EC) += olpc-ec.o
+obj-$(CONFIG_OLPC_XO175_EC) += olpc-xo175-ec.o
diff --git a/drivers/platform/olpc/olpc-ec.c b/drivers/platform/olpc/olpc-ec.c
index 7f25d6c66149..190e4a6186ef 100644
--- a/drivers/platform/olpc/olpc-ec.c
+++ b/drivers/platform/olpc/olpc-ec.c
@@ -15,8 +15,8 @@
#include <linux/workqueue.h>
#include <linux/init.h>
#include <linux/list.h>
+#include <linux/regulator/driver.h>
#include <linux/olpc-ec.h>
-#include <asm/olpc.h>
struct ec_cmd_desc {
u8 cmd;
@@ -32,9 +32,14 @@ struct ec_cmd_desc {
struct olpc_ec_priv {
struct olpc_ec_driver *drv;
+ u8 version;
struct work_struct worker;
struct mutex cmd_lock;
+ /* DCON regulator */
+ struct regulator_dev *dcon_rdev;
+ bool dcon_enabled;
+
/* Pending EC commands */
struct list_head cmd_q;
spinlock_t cmd_q_lock;
@@ -42,6 +47,12 @@ struct olpc_ec_priv {
struct dentry *dbgfs_dir;
/*
+ * EC event mask to be applied during suspend (defining wakeup
+ * sources).
+ */
+ u16 ec_wakeup_mask;
+
+ /*
* Running an EC command while suspending means we don't always finish
* the command before the machine suspends. This means that the EC
* is expecting the command protocol to finish, but we after a period
@@ -118,8 +129,11 @@ int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen)
struct olpc_ec_priv *ec = ec_priv;
struct ec_cmd_desc desc;
- /* Ensure a driver and ec hook have been registered */
- if (WARN_ON(!ec_driver || !ec_driver->ec_cmd))
+ /* Driver not yet registered. */
+ if (!ec_driver)
+ return -EPROBE_DEFER;
+
+ if (WARN_ON(!ec_driver->ec_cmd))
return -ENODEV;
if (!ec)
@@ -149,6 +163,88 @@ int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen)
}
EXPORT_SYMBOL_GPL(olpc_ec_cmd);
+void olpc_ec_wakeup_set(u16 value)
+{
+ struct olpc_ec_priv *ec = ec_priv;
+
+ if (WARN_ON(!ec))
+ return;
+
+ ec->ec_wakeup_mask |= value;
+}
+EXPORT_SYMBOL_GPL(olpc_ec_wakeup_set);
+
+void olpc_ec_wakeup_clear(u16 value)
+{
+ struct olpc_ec_priv *ec = ec_priv;
+
+ if (WARN_ON(!ec))
+ return;
+
+ ec->ec_wakeup_mask &= ~value;
+}
+EXPORT_SYMBOL_GPL(olpc_ec_wakeup_clear);
+
+int olpc_ec_mask_write(u16 bits)
+{
+ struct olpc_ec_priv *ec = ec_priv;
+
+ if (WARN_ON(!ec))
+ return -ENODEV;
+
+ /* EC version 0x5f adds support for wide SCI mask */
+ if (ec->version >= 0x5f) {
+ __be16 ec_word = cpu_to_be16(bits);
+
+ return olpc_ec_cmd(EC_WRITE_EXT_SCI_MASK, (void *)&ec_word, 2, NULL, 0);
+ } else {
+ u8 ec_byte = bits & 0xff;
+
+ return olpc_ec_cmd(EC_WRITE_SCI_MASK, &ec_byte, 1, NULL, 0);
+ }
+}
+EXPORT_SYMBOL_GPL(olpc_ec_mask_write);
+
+/*
+ * Returns true if the compile and runtime configurations allow for EC events
+ * to wake the system.
+ */
+bool olpc_ec_wakeup_available(void)
+{
+ if (WARN_ON(!ec_driver))
+ return false;
+
+ return ec_driver->wakeup_available;
+}
+EXPORT_SYMBOL_GPL(olpc_ec_wakeup_available);
+
+int olpc_ec_sci_query(u16 *sci_value)
+{
+ struct olpc_ec_priv *ec = ec_priv;
+ int ret;
+
+ if (WARN_ON(!ec))
+ return -ENODEV;
+
+ /* EC version 0x5f adds support for wide SCI mask */
+ if (ec->version >= 0x5f) {
+ __be16 ec_word;
+
+ ret = olpc_ec_cmd(EC_EXT_SCI_QUERY, NULL, 0, (void *)&ec_word, 2);
+ if (ret == 0)
+ *sci_value = be16_to_cpu(ec_word);
+ } else {
+ u8 ec_byte;
+
+ ret = olpc_ec_cmd(EC_SCI_QUERY, NULL, 0, &ec_byte, 1);
+ if (ret == 0)
+ *sci_value = ec_byte;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(olpc_ec_sci_query);
+
#ifdef CONFIG_DEBUG_FS
/*
@@ -254,9 +350,61 @@ static struct dentry *olpc_ec_setup_debugfs(void)
#endif /* CONFIG_DEBUG_FS */
+static int olpc_ec_set_dcon_power(struct olpc_ec_priv *ec, bool state)
+{
+ unsigned char ec_byte = state;
+ int ret;
+
+ if (ec->dcon_enabled == state)
+ return 0;
+
+ ret = olpc_ec_cmd(EC_DCON_POWER_MODE, &ec_byte, 1, NULL, 0);
+ if (ret)
+ return ret;
+
+ ec->dcon_enabled = state;
+ return 0;
+}
+
+static int dcon_regulator_enable(struct regulator_dev *rdev)
+{
+ struct olpc_ec_priv *ec = rdev_get_drvdata(rdev);
+
+ return olpc_ec_set_dcon_power(ec, true);
+}
+
+static int dcon_regulator_disable(struct regulator_dev *rdev)
+{
+ struct olpc_ec_priv *ec = rdev_get_drvdata(rdev);
+
+ return olpc_ec_set_dcon_power(ec, false);
+}
+
+static int dcon_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct olpc_ec_priv *ec = rdev_get_drvdata(rdev);
+
+ return ec->dcon_enabled ? 1 : 0;
+}
+
+static struct regulator_ops dcon_regulator_ops = {
+ .enable = dcon_regulator_enable,
+ .disable = dcon_regulator_disable,
+ .is_enabled = dcon_regulator_is_enabled,
+};
+
+static const struct regulator_desc dcon_desc = {
+ .name = "dcon",
+ .id = 0,
+ .ops = &dcon_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+};
+
static int olpc_ec_probe(struct platform_device *pdev)
{
struct olpc_ec_priv *ec;
+ struct regulator_config config = { };
int err;
if (!ec_driver)
@@ -276,14 +424,26 @@ static int olpc_ec_probe(struct platform_device *pdev)
ec_priv = ec;
platform_set_drvdata(pdev, ec);
- err = ec_driver->probe ? ec_driver->probe(pdev) : 0;
+ /* get the EC revision */
+ err = olpc_ec_cmd(EC_FIRMWARE_REV, NULL, 0, &ec->version, 1);
if (err) {
ec_priv = NULL;
kfree(ec);
- } else {
- ec->dbgfs_dir = olpc_ec_setup_debugfs();
+ return err;
}
+ config.dev = pdev->dev.parent;
+ config.driver_data = ec;
+ ec->dcon_enabled = true;
+ ec->dcon_rdev = devm_regulator_register(&pdev->dev, &dcon_desc,
+ &config);
+ if (IS_ERR(ec->dcon_rdev)) {
+ dev_err(&pdev->dev, "failed to register DCON regulator\n");
+ return PTR_ERR(ec->dcon_rdev);
+ }
+
+ ec->dbgfs_dir = olpc_ec_setup_debugfs();
+
return err;
}
@@ -293,6 +453,8 @@ static int olpc_ec_suspend(struct device *dev)
struct olpc_ec_priv *ec = platform_get_drvdata(pdev);
int err = 0;
+ olpc_ec_mask_write(ec->ec_wakeup_mask);
+
if (ec_driver->suspend)
err = ec_driver->suspend(pdev);
if (!err)
diff --git a/drivers/platform/olpc/olpc-xo175-ec.c b/drivers/platform/olpc/olpc-xo175-ec.c
new file mode 100644
index 000000000000..48d6f0d87583
--- /dev/null
+++ b/drivers/platform/olpc/olpc-xo175-ec.c
@@ -0,0 +1,753 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for the OLPC XO-1.75 Embedded Controller.
+ *
+ * The EC protocol is documented at:
+ * http://wiki.laptop.org/go/XO_1.75_HOST_to_EC_Protocol
+ *
+ * Copyright (C) 2010 One Laptop per Child Foundation.
+ * Copyright (C) 2018 Lubomir Rintel <lkundrak@v3.sk>
+ */
+
+#include <linux/completion.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/input.h>
+#include <linux/kfifo.h>
+#include <linux/module.h>
+#include <linux/olpc-ec.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/reboot.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/spi/spi.h>
+
+struct ec_cmd_t {
+ u8 cmd;
+ u8 bytes_returned;
+};
+
+enum ec_chan_t {
+ CHAN_NONE = 0,
+ CHAN_SWITCH,
+ CHAN_CMD_RESP,
+ CHAN_KEYBOARD,
+ CHAN_TOUCHPAD,
+ CHAN_EVENT,
+ CHAN_DEBUG,
+ CHAN_CMD_ERROR,
+};
+
+/*
+ * EC events
+ */
+#define EVENT_AC_CHANGE 1 /* AC plugged/unplugged */
+#define EVENT_BATTERY_STATUS 2 /* Battery low/full/error/gone */
+#define EVENT_BATTERY_CRITICAL 3 /* Battery critical voltage */
+#define EVENT_BATTERY_SOC_CHANGE 4 /* 1% SOC Change */
+#define EVENT_BATTERY_ERROR 5 /* Abnormal error, query for cause */
+#define EVENT_POWER_PRESSED 6 /* Power button was pressed */
+#define EVENT_POWER_PRESS_WAKE 7 /* Woken up with a power button */
+#define EVENT_TIMED_HOST_WAKE 8 /* Host wake timer */
+#define EVENT_OLS_HIGH_LIMIT 9 /* OLS crossed dark threshold */
+#define EVENT_OLS_LOW_LIMIT 10 /* OLS crossed light threshold */
+
+/*
+ * EC commands
+ * (from http://dev.laptop.org/git/users/rsmith/ec-1.75/tree/ec_cmd.h)
+ */
+#define CMD_GET_API_VERSION 0x08 /* out: u8 */
+#define CMD_READ_VOLTAGE 0x10 /* out: u16, *9.76/32, mV */
+#define CMD_READ_CURRENT 0x11 /* out: s16, *15.625/120, mA */
+#define CMD_READ_ACR 0x12 /* out: s16, *6250/15, uAh */
+#define CMD_READ_BATT_TEMPERATURE 0x13 /* out: u16, *100/256, deg C */
+#define CMD_READ_AMBIENT_TEMPERATURE 0x14 /* unimplemented, no hardware */
+#define CMD_READ_BATTERY_STATUS 0x15 /* out: u8, bitmask */
+#define CMD_READ_SOC 0x16 /* out: u8, percentage */
+#define CMD_READ_GAUGE_ID 0x17 /* out: u8 * 8 */
+#define CMD_READ_GAUGE_DATA 0x18 /* in: u8 addr, out: u8 data */
+#define CMD_READ_BOARD_ID 0x19 /* out: u16 (platform id) */
+#define CMD_READ_BATT_ERR_CODE 0x1f /* out: u8, error bitmask */
+#define CMD_SET_DCON_POWER 0x26 /* in: u8 */
+#define CMD_RESET_EC 0x28 /* none */
+#define CMD_READ_BATTERY_TYPE 0x2c /* out: u8 */
+#define CMD_SET_AUTOWAK 0x33 /* out: u8 */
+#define CMD_SET_EC_WAKEUP_TIMER 0x36 /* in: u32, out: ? */
+#define CMD_READ_EXT_SCI_MASK 0x37 /* ? */
+#define CMD_WRITE_EXT_SCI_MASK 0x38 /* ? */
+#define CMD_CLEAR_EC_WAKEUP_TIMER 0x39 /* none */
+#define CMD_ENABLE_RUNIN_DISCHARGE 0x3B /* none */
+#define CMD_DISABLE_RUNIN_DISCHARGE 0x3C /* none */
+#define CMD_READ_MPPT_ACTIVE 0x3d /* out: u8 */
+#define CMD_READ_MPPT_LIMIT 0x3e /* out: u8 */
+#define CMD_SET_MPPT_LIMIT 0x3f /* in: u8 */
+#define CMD_DISABLE_MPPT 0x40 /* none */
+#define CMD_ENABLE_MPPT 0x41 /* none */
+#define CMD_READ_VIN 0x42 /* out: u16 */
+#define CMD_EXT_SCI_QUERY 0x43 /* ? */
+#define RSP_KEYBOARD_DATA 0x48 /* ? */
+#define RSP_TOUCHPAD_DATA 0x49 /* ? */
+#define CMD_GET_FW_VERSION 0x4a /* out: u8 * 16 */
+#define CMD_POWER_CYCLE 0x4b /* none */
+#define CMD_POWER_OFF 0x4c /* none */
+#define CMD_RESET_EC_SOFT 0x4d /* none */
+#define CMD_READ_GAUGE_U16 0x4e /* ? */
+#define CMD_ENABLE_MOUSE 0x4f /* ? */
+#define CMD_ECHO 0x52 /* in: u8 * 5, out: u8 * 5 */
+#define CMD_GET_FW_DATE 0x53 /* out: u8 * 16 */
+#define CMD_GET_FW_USER 0x54 /* out: u8 * 16 */
+#define CMD_TURN_OFF_POWER 0x55 /* none (same as 0x4c) */
+#define CMD_READ_OLS 0x56 /* out: u16 */
+#define CMD_OLS_SMT_LEDON 0x57 /* none */
+#define CMD_OLS_SMT_LEDOFF 0x58 /* none */
+#define CMD_START_OLS_ASSY 0x59 /* none */
+#define CMD_STOP_OLS_ASSY 0x5a /* none */
+#define CMD_OLS_SMTTEST_STOP 0x5b /* none */
+#define CMD_READ_VIN_SCALED 0x5c /* out: u16 */
+#define CMD_READ_BAT_MIN_W 0x5d /* out: u16 */
+#define CMD_READ_BAR_MAX_W 0x5e /* out: u16 */
+#define CMD_RESET_BAT_MINMAX_W 0x5f /* none */
+#define CMD_READ_LOCATION 0x60 /* in: u16 addr, out: u8 data */
+#define CMD_WRITE_LOCATION 0x61 /* in: u16 addr, u8 data */
+#define CMD_KEYBOARD_CMD 0x62 /* in: u8, out: ? */
+#define CMD_TOUCHPAD_CMD 0x63 /* in: u8, out: ? */
+#define CMD_GET_FW_HASH 0x64 /* out: u8 * 16 */
+#define CMD_SUSPEND_HINT 0x65 /* in: u8 */
+#define CMD_ENABLE_WAKE_TIMER 0x66 /* in: u8 */
+#define CMD_SET_WAKE_TIMER 0x67 /* in: 32 */
+#define CMD_ENABLE_WAKE_AUTORESET 0x68 /* in: u8 */
+#define CMD_OLS_SET_LIMITS 0x69 /* in: u16, u16 */
+#define CMD_OLS_GET_LIMITS 0x6a /* out: u16, u16 */
+#define CMD_OLS_SET_CEILING 0x6b /* in: u16 */
+#define CMD_OLS_GET_CEILING 0x6c /* out: u16 */
+
+/*
+ * Accepted EC commands, and how many bytes they return. There are plenty
+ * of EC commands that are no longer implemented, or are implemented only on
+ * certain older boards.
+ */
+static const struct ec_cmd_t olpc_xo175_ec_cmds[] = {
+ { CMD_GET_API_VERSION, 1 },
+ { CMD_READ_VOLTAGE, 2 },
+ { CMD_READ_CURRENT, 2 },
+ { CMD_READ_ACR, 2 },
+ { CMD_READ_BATT_TEMPERATURE, 2 },
+ { CMD_READ_BATTERY_STATUS, 1 },
+ { CMD_READ_SOC, 1 },
+ { CMD_READ_GAUGE_ID, 8 },
+ { CMD_READ_GAUGE_DATA, 1 },
+ { CMD_READ_BOARD_ID, 2 },
+ { CMD_READ_BATT_ERR_CODE, 1 },
+ { CMD_SET_DCON_POWER, 0 },
+ { CMD_RESET_EC, 0 },
+ { CMD_READ_BATTERY_TYPE, 1 },
+ { CMD_ENABLE_RUNIN_DISCHARGE, 0 },
+ { CMD_DISABLE_RUNIN_DISCHARGE, 0 },
+ { CMD_READ_MPPT_ACTIVE, 1 },
+ { CMD_READ_MPPT_LIMIT, 1 },
+ { CMD_SET_MPPT_LIMIT, 0 },
+ { CMD_DISABLE_MPPT, 0 },
+ { CMD_ENABLE_MPPT, 0 },
+ { CMD_READ_VIN, 2 },
+ { CMD_GET_FW_VERSION, 16 },
+ { CMD_POWER_CYCLE, 0 },
+ { CMD_POWER_OFF, 0 },
+ { CMD_RESET_EC_SOFT, 0 },
+ { CMD_ECHO, 5 },
+ { CMD_GET_FW_DATE, 16 },
+ { CMD_GET_FW_USER, 16 },
+ { CMD_TURN_OFF_POWER, 0 },
+ { CMD_READ_OLS, 2 },
+ { CMD_OLS_SMT_LEDON, 0 },
+ { CMD_OLS_SMT_LEDOFF, 0 },
+ { CMD_START_OLS_ASSY, 0 },
+ { CMD_STOP_OLS_ASSY, 0 },
+ { CMD_OLS_SMTTEST_STOP, 0 },
+ { CMD_READ_VIN_SCALED, 2 },
+ { CMD_READ_BAT_MIN_W, 2 },
+ { CMD_READ_BAR_MAX_W, 2 },
+ { CMD_RESET_BAT_MINMAX_W, 0 },
+ { CMD_READ_LOCATION, 1 },
+ { CMD_WRITE_LOCATION, 0 },
+ { CMD_GET_FW_HASH, 16 },
+ { CMD_SUSPEND_HINT, 0 },
+ { CMD_ENABLE_WAKE_TIMER, 0 },
+ { CMD_SET_WAKE_TIMER, 0 },
+ { CMD_ENABLE_WAKE_AUTORESET, 0 },
+ { CMD_OLS_SET_LIMITS, 0 },
+ { CMD_OLS_GET_LIMITS, 4 },
+ { CMD_OLS_SET_CEILING, 0 },
+ { CMD_OLS_GET_CEILING, 2 },
+ { CMD_READ_EXT_SCI_MASK, 2 },
+ { CMD_WRITE_EXT_SCI_MASK, 0 },
+
+ { }
+};
+
+#define EC_MAX_CMD_DATA_LEN 5
+#define EC_MAX_RESP_LEN 16
+
+#define LOG_BUF_SIZE 128
+
+#define PM_WAKEUP_TIME 1000
+
+#define EC_ALL_EVENTS GENMASK(15, 0)
+
+enum ec_state_t {
+ CMD_STATE_IDLE = 0,
+ CMD_STATE_WAITING_FOR_SWITCH,
+ CMD_STATE_CMD_IN_TX_FIFO,
+ CMD_STATE_CMD_SENT,
+ CMD_STATE_RESP_RECEIVED,
+ CMD_STATE_ERROR_RECEIVED,
+};
+
+struct olpc_xo175_ec_cmd {
+ u8 command;
+ u8 nr_args;
+ u8 data_len;
+ u8 args[EC_MAX_CMD_DATA_LEN];
+};
+
+struct olpc_xo175_ec_resp {
+ u8 channel;
+ u8 byte;
+};
+
+struct olpc_xo175_ec {
+ bool suspended;
+
+ /* SPI related stuff. */
+ struct spi_device *spi;
+ struct spi_transfer xfer;
+ struct spi_message msg;
+ union {
+ struct olpc_xo175_ec_cmd cmd;
+ struct olpc_xo175_ec_resp resp;
+ } tx_buf, rx_buf;
+
+ /* GPIO for the CMD signals. */
+ struct gpio_desc *gpio_cmd;
+
+ /* Command handling related state. */
+ spinlock_t cmd_state_lock;
+ int cmd_state;
+ bool cmd_running;
+ struct completion cmd_done;
+ struct olpc_xo175_ec_cmd cmd;
+ u8 resp_data[EC_MAX_RESP_LEN];
+ int expected_resp_len;
+ int resp_len;
+
+ /* Power button. */
+ struct input_dev *pwrbtn;
+
+ /* Debug handling. */
+ char logbuf[LOG_BUF_SIZE];
+ int logbuf_len;
+};
+
+static struct platform_device *olpc_ec;
+
+static int olpc_xo175_ec_resp_len(u8 cmd)
+{
+ const struct ec_cmd_t *p;
+
+ for (p = olpc_xo175_ec_cmds; p->cmd; p++) {
+ if (p->cmd == cmd)
+ return p->bytes_returned;
+ }
+
+ return -EINVAL;
+}
+
+static void olpc_xo175_ec_flush_logbuf(struct olpc_xo175_ec *priv)
+{
+ dev_dbg(&priv->spi->dev, "got debug string [%*pE]\n",
+ priv->logbuf_len, priv->logbuf);
+ priv->logbuf_len = 0;
+}
+
+static void olpc_xo175_ec_complete(void *arg);
+
+static void olpc_xo175_ec_send_command(struct olpc_xo175_ec *priv, void *cmd,
+ size_t cmdlen)
+{
+ int ret;
+
+ memcpy(&priv->tx_buf, cmd, cmdlen);
+ priv->xfer.len = cmdlen;
+
+ spi_message_init_with_transfers(&priv->msg, &priv->xfer, 1);
+
+ priv->msg.complete = olpc_xo175_ec_complete;
+ priv->msg.context = priv;
+
+ ret = spi_async(priv->spi, &priv->msg);
+ if (ret)
+ dev_err(&priv->spi->dev, "spi_async() failed %d\n", ret);
+}
+
+static void olpc_xo175_ec_read_packet(struct olpc_xo175_ec *priv)
+{
+ u8 nonce[] = {0xA5, 0x5A};
+
+ olpc_xo175_ec_send_command(priv, nonce, sizeof(nonce));
+}
+
+static void olpc_xo175_ec_complete(void *arg)
+{
+ struct olpc_xo175_ec *priv = arg;
+ struct device *dev = &priv->spi->dev;
+ struct power_supply *psy;
+ unsigned long flags;
+ u8 channel;
+ u8 byte;
+ int ret;
+
+ ret = priv->msg.status;
+ if (ret) {
+ dev_err(dev, "SPI transfer failed: %d\n", ret);
+
+ spin_lock_irqsave(&priv->cmd_state_lock, flags);
+ if (priv->cmd_running) {
+ priv->resp_len = 0;
+ priv->cmd_state = CMD_STATE_ERROR_RECEIVED;
+ complete(&priv->cmd_done);
+ }
+ spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
+
+ if (ret != -EINTR)
+ olpc_xo175_ec_read_packet(priv);
+
+ return;
+ }
+
+ channel = priv->rx_buf.resp.channel;
+ byte = priv->rx_buf.resp.byte;
+
+ switch (channel) {
+ case CHAN_NONE:
+ spin_lock_irqsave(&priv->cmd_state_lock, flags);
+
+ if (!priv->cmd_running) {
+ /* We can safely ignore these */
+ dev_err(dev, "spurious FIFO read packet\n");
+ spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
+ return;
+ }
+
+ priv->cmd_state = CMD_STATE_CMD_SENT;
+ if (!priv->expected_resp_len)
+ complete(&priv->cmd_done);
+ olpc_xo175_ec_read_packet(priv);
+
+ spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
+ return;
+
+ case CHAN_SWITCH:
+ spin_lock_irqsave(&priv->cmd_state_lock, flags);
+
+ if (!priv->cmd_running) {
+ /* Just go with the flow */
+ dev_err(dev, "spurious SWITCH packet\n");
+ memset(&priv->cmd, 0, sizeof(priv->cmd));
+ priv->cmd.command = CMD_ECHO;
+ }
+
+ priv->cmd_state = CMD_STATE_CMD_IN_TX_FIFO;
+
+ /* Throw command into TxFIFO */
+ gpiod_set_value_cansleep(priv->gpio_cmd, 0);
+ olpc_xo175_ec_send_command(priv, &priv->cmd, sizeof(priv->cmd));
+
+ spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
+ return;
+
+ case CHAN_CMD_RESP:
+ spin_lock_irqsave(&priv->cmd_state_lock, flags);
+
+ if (!priv->cmd_running) {
+ dev_err(dev, "spurious response packet\n");
+ } else if (priv->resp_len >= priv->expected_resp_len) {
+ dev_err(dev, "too many response packets\n");
+ } else {
+ priv->resp_data[priv->resp_len++] = byte;
+ if (priv->resp_len == priv->expected_resp_len) {
+ priv->cmd_state = CMD_STATE_RESP_RECEIVED;
+ complete(&priv->cmd_done);
+ }
+ }
+
+ spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
+ break;
+
+ case CHAN_CMD_ERROR:
+ spin_lock_irqsave(&priv->cmd_state_lock, flags);
+
+ if (!priv->cmd_running) {
+ dev_err(dev, "spurious cmd error packet\n");
+ } else {
+ priv->resp_data[0] = byte;
+ priv->resp_len = 1;
+ priv->cmd_state = CMD_STATE_ERROR_RECEIVED;
+ complete(&priv->cmd_done);
+ }
+ spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
+ break;
+
+ case CHAN_KEYBOARD:
+ dev_warn(dev, "keyboard is not supported\n");
+ break;
+
+ case CHAN_TOUCHPAD:
+ dev_warn(dev, "touchpad is not supported\n");
+ break;
+
+ case CHAN_EVENT:
+ dev_dbg(dev, "got event %.2x\n", byte);
+ switch (byte) {
+ case EVENT_AC_CHANGE:
+ psy = power_supply_get_by_name("olpc-ac");
+ if (psy) {
+ power_supply_changed(psy);
+ power_supply_put(psy);
+ }
+ break;
+ case EVENT_BATTERY_STATUS:
+ case EVENT_BATTERY_CRITICAL:
+ case EVENT_BATTERY_SOC_CHANGE:
+ case EVENT_BATTERY_ERROR:
+ psy = power_supply_get_by_name("olpc-battery");
+ if (psy) {
+ power_supply_changed(psy);
+ power_supply_put(psy);
+ }
+ break;
+ case EVENT_POWER_PRESSED:
+ input_report_key(priv->pwrbtn, KEY_POWER, 1);
+ input_sync(priv->pwrbtn);
+ input_report_key(priv->pwrbtn, KEY_POWER, 0);
+ input_sync(priv->pwrbtn);
+ /* fall through */
+ case EVENT_POWER_PRESS_WAKE:
+ case EVENT_TIMED_HOST_WAKE:
+ pm_wakeup_event(priv->pwrbtn->dev.parent,
+ PM_WAKEUP_TIME);
+ break;
+ default:
+ dev_dbg(dev, "ignored unknown event %.2x\n", byte);
+ break;
+ }
+ break;
+
+ case CHAN_DEBUG:
+ if (byte == '\n') {
+ olpc_xo175_ec_flush_logbuf(priv);
+ } else if (isprint(byte)) {
+ priv->logbuf[priv->logbuf_len++] = byte;
+ if (priv->logbuf_len == LOG_BUF_SIZE)
+ olpc_xo175_ec_flush_logbuf(priv);
+ }
+ break;
+
+ default:
+ dev_warn(dev, "unknown channel: %d, %.2x\n", channel, byte);
+ break;
+ }
+
+ /* Most non-command packets get the TxFIFO refilled and an ACK. */
+ olpc_xo175_ec_read_packet(priv);
+}
+
+/*
+ * This function is protected with a mutex. We can safely assume that
+ * there will be only one instance of this function running at a time.
+ * One of the ways in which we enforce this is by waiting until we get
+ * all response bytes back from the EC, rather than just the number that
+ * the caller requests (otherwise, we might start a new command while an
+ * old command's response bytes are still incoming).
+ */
+static int olpc_xo175_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *resp,
+ size_t resp_len, void *ec_cb_arg)
+{
+ struct olpc_xo175_ec *priv = ec_cb_arg;
+ struct device *dev = &priv->spi->dev;
+ unsigned long flags;
+ size_t nr_bytes;
+ int ret = 0;
+
+ dev_dbg(dev, "CMD %x, %zd bytes expected\n", cmd, resp_len);
+
+ if (inlen > 5) {
+ dev_err(dev, "command len %zd too big!\n", resp_len);
+ return -EOVERFLOW;
+ }
+
+ /* Suspending in the middle of an EC command hoses things badly! */
+ if (WARN_ON(priv->suspended))
+ return -EBUSY;
+
+ /* Ensure a valid command and return bytes */
+ ret = olpc_xo175_ec_resp_len(cmd);
+ if (ret < 0) {
+ dev_err_ratelimited(dev, "unknown command 0x%x\n", cmd);
+
+ /*
+ * Assume the best in our callers, and allow unknown commands
+ * through. I'm not the charitable type, but it was beaten
+ * into me. Just maintain a minimum standard of sanity.
+ */
+ if (resp_len > sizeof(priv->resp_data)) {
+ dev_err(dev, "response too big: %zd!\n", resp_len);
+ return -EOVERFLOW;
+ }
+ nr_bytes = resp_len;
+ } else {
+ nr_bytes = (size_t)ret;
+ ret = 0;
+ }
+ resp_len = min(resp_len, nr_bytes);
+
+ spin_lock_irqsave(&priv->cmd_state_lock, flags);
+
+ /* Initialize the state machine */
+ init_completion(&priv->cmd_done);
+ priv->cmd_running = true;
+ priv->cmd_state = CMD_STATE_WAITING_FOR_SWITCH;
+ memset(&priv->cmd, 0, sizeof(priv->cmd));
+ priv->cmd.command = cmd;
+ priv->cmd.nr_args = inlen;
+ priv->cmd.data_len = 0;
+ memcpy(priv->cmd.args, inbuf, inlen);
+ priv->expected_resp_len = nr_bytes;
+ priv->resp_len = 0;
+
+ /* Tickle the cmd gpio to get things started */
+ gpiod_set_value_cansleep(priv->gpio_cmd, 1);
+
+ spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
+
+ /* The irq handler should do the rest */
+ if (!wait_for_completion_timeout(&priv->cmd_done,
+ msecs_to_jiffies(4000))) {
+ dev_err(dev, "EC cmd error: timeout in STATE %d\n",
+ priv->cmd_state);
+ gpiod_set_value_cansleep(priv->gpio_cmd, 0);
+ spi_slave_abort(priv->spi);
+ olpc_xo175_ec_read_packet(priv);
+ return -ETIMEDOUT;
+ }
+
+ spin_lock_irqsave(&priv->cmd_state_lock, flags);
+
+ /* Deal with the results. */
+ if (priv->cmd_state == CMD_STATE_ERROR_RECEIVED) {
+ /* EC-provided error is in the single response byte */
+ dev_err(dev, "command 0x%x returned error 0x%x\n",
+ cmd, priv->resp_data[0]);
+ ret = -EREMOTEIO;
+ } else if (priv->resp_len != nr_bytes) {
+ dev_err(dev, "command 0x%x returned %d bytes, expected %zd bytes\n",
+ cmd, priv->resp_len, nr_bytes);
+ ret = -EREMOTEIO;
+ } else {
+ /*
+ * We may have 8 bytes in priv->resp, but we only care about
+ * what we've been asked for. If the caller asked for only 2
+ * bytes, give them that. We've guaranteed that
+ * resp_len <= priv->resp_len and priv->resp_len == nr_bytes.
+ */
+ memcpy(resp, priv->resp_data, resp_len);
+ }
+
+ /* This should already be low, but just in case. */
+ gpiod_set_value_cansleep(priv->gpio_cmd, 0);
+ priv->cmd_running = false;
+
+ spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
+
+ return ret;
+}
+
+static int olpc_xo175_ec_set_event_mask(unsigned int mask)
+{
+ u8 args[2];
+
+ args[0] = mask >> 0;
+ args[1] = mask >> 8;
+ return olpc_ec_cmd(CMD_WRITE_EXT_SCI_MASK, args, 2, NULL, 0);
+}
+
+static void olpc_xo175_ec_power_off(void)
+{
+ while (1) {
+ olpc_ec_cmd(CMD_POWER_OFF, NULL, 0, NULL, 0);
+ mdelay(1000);
+ }
+}
+
+static int __maybe_unused olpc_xo175_ec_suspend(struct device *dev)
+{
+ struct olpc_xo175_ec *priv = dev_get_drvdata(dev);
+ static struct {
+ u8 suspend;
+ u32 suspend_count;
+ } __packed hintargs;
+ static unsigned int suspend_count;
+
+ /*
+ * SOC_SLEEP is not wired to the EC on B3 and earlier boards.
+ * This command lets the EC know instead. The suspend count doesn't seem
+ * to be used anywhere but in the EC debug output.
+ */
+ hintargs.suspend = 1;
+ hintargs.suspend_count = suspend_count++;
+ olpc_ec_cmd(CMD_SUSPEND_HINT, (void *)&hintargs, sizeof(hintargs),
+ NULL, 0);
+
+ /*
+ * After we've sent the suspend hint, don't allow further EC commands
+ * to be run until we've resumed. Userspace tasks should be frozen,
+ * but kernel threads and interrupts could still schedule EC commands.
+ */
+ priv->suspended = true;
+
+ return 0;
+}
+
+static int __maybe_unused olpc_xo175_ec_resume_noirq(struct device *dev)
+{
+ struct olpc_xo175_ec *priv = dev_get_drvdata(dev);
+
+ priv->suspended = false;
+
+ return 0;
+}
+
+static int __maybe_unused olpc_xo175_ec_resume(struct device *dev)
+{
+ u8 x = 0;
+
+ /*
+ * The resume hint is only needed if no other commands are
+ * being sent during resume. all it does is tell the EC
+ * the SoC is definitely awake.
+ */
+ olpc_ec_cmd(CMD_SUSPEND_HINT, &x, 1, NULL, 0);
+
+ /* Enable all EC events while we're awake */
+ olpc_xo175_ec_set_event_mask(EC_ALL_EVENTS);
+
+ return 0;
+}
+
+static struct olpc_ec_driver olpc_xo175_ec_driver = {
+ .ec_cmd = olpc_xo175_ec_cmd,
+};
+
+static int olpc_xo175_ec_remove(struct spi_device *spi)
+{
+ if (pm_power_off == olpc_xo175_ec_power_off)
+ pm_power_off = NULL;
+
+ spi_slave_abort(spi);
+
+ platform_device_unregister(olpc_ec);
+ olpc_ec = NULL;
+
+ return 0;
+}
+
+static int olpc_xo175_ec_probe(struct spi_device *spi)
+{
+ struct olpc_xo175_ec *priv;
+ int ret;
+
+ if (olpc_ec) {
+ dev_err(&spi->dev, "OLPC EC already registered.\n");
+ return -EBUSY;
+ }
+
+ priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->gpio_cmd = devm_gpiod_get(&spi->dev, "cmd", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpio_cmd)) {
+ dev_err(&spi->dev, "failed to get cmd gpio: %ld\n",
+ PTR_ERR(priv->gpio_cmd));
+ return PTR_ERR(priv->gpio_cmd);
+ }
+
+ priv->spi = spi;
+
+ spin_lock_init(&priv->cmd_state_lock);
+ priv->cmd_state = CMD_STATE_IDLE;
+ init_completion(&priv->cmd_done);
+
+ priv->logbuf_len = 0;
+
+ /* Set up power button input device */
+ priv->pwrbtn = devm_input_allocate_device(&spi->dev);
+ if (!priv->pwrbtn)
+ return -ENOMEM;
+ priv->pwrbtn->name = "Power Button";
+ priv->pwrbtn->dev.parent = &spi->dev;
+ input_set_capability(priv->pwrbtn, EV_KEY, KEY_POWER);
+ ret = input_register_device(priv->pwrbtn);
+ if (ret) {
+ dev_err(&spi->dev, "error registering input device: %d\n", ret);
+ return ret;
+ }
+
+ spi_set_drvdata(spi, priv);
+
+ priv->xfer.rx_buf = &priv->rx_buf;
+ priv->xfer.tx_buf = &priv->tx_buf;
+
+ olpc_xo175_ec_read_packet(priv);
+
+ olpc_ec_driver_register(&olpc_xo175_ec_driver, priv);
+ olpc_ec = platform_device_register_resndata(&spi->dev, "olpc-ec", -1,
+ NULL, 0, NULL, 0);
+
+ /* Enable all EC events while we're awake */
+ olpc_xo175_ec_set_event_mask(EC_ALL_EVENTS);
+
+ if (pm_power_off == NULL)
+ pm_power_off = olpc_xo175_ec_power_off;
+
+ dev_info(&spi->dev, "OLPC XO-1.75 Embedded Controller driver\n");
+
+ return 0;
+}
+
+static const struct dev_pm_ops olpc_xo175_ec_pm_ops = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, olpc_xo175_ec_resume_noirq)
+ SET_RUNTIME_PM_OPS(olpc_xo175_ec_suspend, olpc_xo175_ec_resume, NULL)
+};
+
+static const struct of_device_id olpc_xo175_ec_of_match[] = {
+ { .compatible = "olpc,xo1.75-ec" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, olpc_xo175_ec_of_match);
+
+static struct spi_driver olpc_xo175_ec_spi_driver = {
+ .driver = {
+ .name = "olpc-xo175-ec",
+ .of_match_table = olpc_xo175_ec_of_match,
+ .pm = &olpc_xo175_ec_pm_ops,
+ },
+ .probe = olpc_xo175_ec_probe,
+ .remove = olpc_xo175_ec_remove,
+};
+module_spi_driver(olpc_xo175_ec_spi_driver);
+
+MODULE_DESCRIPTION("OLPC XO-1.75 Embedded Controller driver");
+MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>"); /* Functionality */
+MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); /* Bugs */
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 6053d0158b3b..cc29fe79c283 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -778,6 +778,16 @@ config INTEL_WMI_THUNDERBOLT
To compile this driver as a module, choose M here: the module will
be called intel-wmi-thunderbolt.
+config XIAOMI_WMI
+ tristate "Xiaomi WMI key driver"
+ depends on ACPI_WMI
+ depends on INPUT
+ help
+ Say Y here if you want to support WMI-based keys on Xiaomi notebooks.
+
+ To compile this driver as a module, choose M here: the module will
+ be called xiaomi-wmi.
+
config MSI_WMI
tristate "MSI WMI extras"
depends on ACPI_WMI
@@ -903,7 +913,6 @@ config TOSHIBA_WMI
config ACPI_CMPC
tristate "CMPC Laptop Extras"
depends on ACPI && INPUT
- depends on BACKLIGHT_LCD_SUPPORT
depends on RFKILL || RFKILL=n
select BACKLIGHT_CLASS_DEVICE
help
@@ -1127,7 +1136,6 @@ config INTEL_OAKTRAIL
config SAMSUNG_Q10
tristate "Samsung Q10 Extras"
depends on ACPI
- depends on BACKLIGHT_LCD_SUPPORT
select BACKLIGHT_CLASS_DEVICE
---help---
This driver provides support for backlight control on Samsung Q10
@@ -1314,7 +1322,7 @@ config HUAWEI_WMI
config PCENGINES_APU2
tristate "PC Engines APUv2/3 front button and LEDs driver"
- depends on INPUT && INPUT_KEYBOARD
+ depends on INPUT && INPUT_KEYBOARD && GPIOLIB
depends on LEDS_CLASS
select GPIO_AMD_FCH
select KEYBOARD_GPIO_POLLED
@@ -1326,6 +1334,8 @@ config PCENGINES_APU2
To compile this driver as a module, choose M here: the module
will be called pcengines-apuv2.
+source "drivers/platform/x86/intel_speed_select_if/Kconfig"
+
endif # X86_PLATFORM_DEVICES
config PMC_ATOM
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 87b0069bd781..415104033060 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o
obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o
obj-$(CONFIG_INTEL_WMI_THUNDERBOLT) += intel-wmi-thunderbolt.o
+obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o
# toshiba_acpi must link after wmi to ensure that wmi devices are found
# before toshiba_acpi initializes
@@ -89,7 +90,7 @@ obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += intel_bxtwc_tmu.o
obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \
intel_telemetry_pltdrv.o \
intel_telemetry_debugfs.o
-obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o
+obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv.o
obj-$(CONFIG_PMC_ATOM) += pmc_atom.o
obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o
obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o
@@ -98,3 +99,4 @@ obj-$(CONFIG_INTEL_MRFLD_PWRBTN) += intel_mrfld_pwrbtn.o
obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o
obj-$(CONFIG_INTEL_ATOMISP2_PM) += intel_atomisp2_pm.o
obj-$(CONFIG_PCENGINES_APU2) += pcengines-apuv2.o
+obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += intel_speed_select_if/
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index 521b526cd467..62b54e137231 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -259,7 +259,6 @@ struct acer_data {
struct acer_debug {
struct dentry *root;
- struct dentry *devices;
u32 wmid_devices;
};
@@ -1002,6 +1001,7 @@ static acpi_status WMID_get_u32(u32 *value, u32 cap)
*value = tmp & 0x1;
return 0;
}
+ /* fall through */
default:
return AE_ERROR;
}
@@ -1328,6 +1328,7 @@ static acpi_status get_u32(u32 *value, u32 cap)
status = AMW0_get_u32(value, cap);
break;
}
+ /* fall through */
case ACER_WMID:
status = WMID_get_u32(value, cap);
break;
@@ -1370,6 +1371,7 @@ static acpi_status set_u32(u32 value, u32 cap)
return AMW0_set_u32(value, cap);
}
+ /* fall through */
case ACER_WMID:
return WMID_set_u32(value, cap);
case ACER_WMID_v2:
@@ -1379,6 +1381,7 @@ static acpi_status set_u32(u32 value, u32 cap)
return wmid_v2_set_u32(value, cap);
else if (wmi_has_guid(WMID_GUID2))
return WMID_set_u32(value, cap);
+ /* fall through */
default:
return AE_BAD_PARAMETER;
}
@@ -2148,29 +2151,15 @@ static struct platform_device *acer_platform_device;
static void remove_debugfs(void)
{
- debugfs_remove(interface->debug.devices);
- debugfs_remove(interface->debug.root);
+ debugfs_remove_recursive(interface->debug.root);
}
-static int __init create_debugfs(void)
+static void __init create_debugfs(void)
{
interface->debug.root = debugfs_create_dir("acer-wmi", NULL);
- if (!interface->debug.root) {
- pr_err("Failed to create debugfs directory");
- return -ENOMEM;
- }
- interface->debug.devices = debugfs_create_u32("devices", S_IRUGO,
- interface->debug.root,
- &interface->debug.wmid_devices);
- if (!interface->debug.devices)
- goto error_debugfs;
-
- return 0;
-
-error_debugfs:
- remove_debugfs();
- return -ENOMEM;
+ debugfs_create_u32("devices", S_IRUGO, interface->debug.root,
+ &interface->debug.wmid_devices);
}
static int __init acer_wmi_init(void)
@@ -2300,9 +2289,7 @@ static int __init acer_wmi_init(void)
if (wmi_has_guid(WMID_GUID2)) {
interface->debug.wmid_devices = get_wmid_devices();
- err = create_debugfs();
- if (err)
- goto error_create_debugfs;
+ create_debugfs();
}
/* Override any initial settings with values from the commandline */
@@ -2310,8 +2297,6 @@ static int __init acer_wmi_init(void)
return 0;
-error_create_debugfs:
- platform_device_del(acer_platform_device);
error_device_add:
platform_device_put(acer_platform_device);
error_device_alloc:
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c
index 8d9e30dbb5af..2ebde0174937 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -463,6 +463,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
{ KE_IGNORE, 0x6E, }, /* Low Battery notification */
{ KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */
+ { KE_KEY, 0x7c, { KEY_MICMUTE } },
{ KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */
{ KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */
{ KE_KEY, 0x82, { KEY_CAMERA } },
@@ -477,7 +478,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */
{ KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI */
{ KE_KEY, 0x95, { KEY_MEDIA } },
- { KE_KEY, 0x99, { KEY_PHONE } },
+ { KE_KEY, 0x99, { KEY_PHONE } }, /* Conflicts with fan mode switch */
{ KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */
{ KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */
{ KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 9b18a184e0aa..18f3a8bad52f 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -57,6 +57,7 @@ MODULE_LICENSE("GPL");
#define NOTIFY_KBD_BRTUP 0xc4
#define NOTIFY_KBD_BRTDWN 0xc5
#define NOTIFY_KBD_BRTTOGGLE 0xc7
+#define NOTIFY_KBD_FBM 0x99
#define ASUS_WMI_FNLOCK_BIOS_DISABLED BIT(0)
@@ -67,9 +68,27 @@ MODULE_LICENSE("GPL");
#define ASUS_FAN_CTRL_MANUAL 1
#define ASUS_FAN_CTRL_AUTO 2
+#define ASUS_FAN_MODE_NORMAL 0
+#define ASUS_FAN_MODE_OVERBOOST 1
+#define ASUS_FAN_MODE_OVERBOOST_MASK 0x01
+#define ASUS_FAN_MODE_SILENT 2
+#define ASUS_FAN_MODE_SILENT_MASK 0x02
+#define ASUS_FAN_MODES_MASK 0x03
+
#define USB_INTEL_XUSB2PR 0xD0
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
+#define ASUS_ACPI_UID_ASUSWMI "ASUSWMI"
+#define ASUS_ACPI_UID_ATK "ATK"
+
+#define WMI_EVENT_QUEUE_SIZE 0x10
+#define WMI_EVENT_QUEUE_END 0x1
+#define WMI_EVENT_MASK 0xFFFF
+/* The WMI hotkey event value is always the same. */
+#define WMI_EVENT_VALUE_ATK 0xFF
+
+#define WMI_EVENT_MASK 0xFFFF
+
static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL };
static bool ashs_present(void)
@@ -85,6 +104,7 @@ static bool ashs_present(void)
struct bios_args {
u32 arg0;
u32 arg1;
+ u32 arg2; /* At least TUF Gaming series uses 3 dword input buffer. */
} __packed;
/*
@@ -132,6 +152,7 @@ struct asus_wmi {
int dsts_id;
int spec;
int sfun;
+ bool wmi_event_queue;
struct input_dev *inputdev;
struct backlight_device *backlight_device;
@@ -161,6 +182,10 @@ struct asus_wmi {
int asus_hwmon_num_fans;
int asus_hwmon_pwm;
+ bool fan_mode_available;
+ u8 fan_mode_mask;
+ u8 fan_mode;
+
struct hotplug_slot hotplug_slot;
struct mutex hotplug_lock;
struct mutex wmi_lock;
@@ -174,6 +199,8 @@ struct asus_wmi {
struct asus_wmi_driver *driver;
};
+/* Input **********************************************************************/
+
static int asus_wmi_input_init(struct asus_wmi *asus)
{
int err;
@@ -211,11 +238,15 @@ static void asus_wmi_input_exit(struct asus_wmi *asus)
asus->inputdev = NULL;
}
-int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval)
+/* WMI ************************************************************************/
+
+static int asus_wmi_evaluate_method3(u32 method_id,
+ u32 arg0, u32 arg1, u32 arg2, u32 *retval)
{
struct bios_args args = {
.arg0 = arg0,
.arg1 = arg1,
+ .arg2 = arg2,
};
struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -227,7 +258,7 @@ int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval)
&input, &output);
if (ACPI_FAILURE(status))
- goto exit;
+ return -EIO;
obj = (union acpi_object *)output.pointer;
if (obj && obj->type == ACPI_TYPE_INTEGER)
@@ -238,15 +269,16 @@ int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval)
kfree(obj);
-exit:
- if (ACPI_FAILURE(status))
- return -EIO;
-
if (tmp == ASUS_WMI_UNSUPPORTED_METHOD)
return -ENODEV;
return 0;
}
+
+int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval)
+{
+ return asus_wmi_evaluate_method3(method_id, arg0, arg1, 0, retval);
+}
EXPORT_SYMBOL_GPL(asus_wmi_evaluate_method);
static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args)
@@ -320,9 +352,8 @@ static int asus_wmi_get_devstate_simple(struct asus_wmi *asus, u32 dev_id)
ASUS_WMI_DSTS_STATUS_BIT);
}
-/*
- * LEDs
- */
+/* LEDs ***********************************************************************/
+
/*
* These functions actually update the LED's, and are called from a
* workqueue. By doing this as separate work rather than when the LED
@@ -427,6 +458,10 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value)
static void kbd_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
+ /* Prevent disabling keyboard backlight on module unregister */
+ if (led_cdev->flags & LED_UNREGISTERING)
+ return;
+
do_kbd_led_set(led_cdev, value);
}
@@ -582,8 +617,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
goto error;
}
- led_val = kbd_led_read(asus, NULL, NULL);
- if (led_val >= 0) {
+ if (!kbd_led_read(asus, &led_val, NULL)) {
asus->kbd_led_wk = led_val;
asus->kbd_led.name = "asus::kbd_backlight";
asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
@@ -633,6 +667,7 @@ error:
return rv;
}
+/* RF *************************************************************************/
/*
* PCI hotplug (for wlan rfkill)
@@ -1055,6 +1090,8 @@ exit:
return result;
}
+/* Quirks *********************************************************************/
+
static void asus_wmi_set_xusb2pr(struct asus_wmi *asus)
{
struct pci_dev *xhci_pdev;
@@ -1087,9 +1124,8 @@ static void asus_wmi_set_als(void)
asus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL);
}
-/*
- * Hwmon device
- */
+/* Hwmon device ***************************************************************/
+
static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan,
int *speed)
{
@@ -1353,8 +1389,7 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
struct attribute *attr, int idx)
{
struct device *dev = container_of(kobj, struct device, kobj);
- struct platform_device *pdev = to_platform_device(dev->parent);
- struct asus_wmi *asus = platform_get_drvdata(pdev);
+ struct asus_wmi *asus = dev_get_drvdata(dev->parent);
int dev_id = -1;
int fan_attr = -1;
u32 value = ASUS_WMI_UNSUPPORTED_METHOD;
@@ -1395,8 +1430,11 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
else
ok = fan_attr <= asus->asus_hwmon_num_fans;
} else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) {
- /* If value is zero, something is clearly wrong */
- if (!value)
+ /*
+ * If the temperature value in deci-Kelvin is near the absolute
+ * zero temperature, something is clearly wrong
+ */
+ if (value == 0 || value == 1)
ok = false;
} else if (fan_attr <= asus->asus_hwmon_num_fans && fan_attr != -1) {
ok = true;
@@ -1415,11 +1453,12 @@ __ATTRIBUTE_GROUPS(hwmon_attribute);
static int asus_wmi_hwmon_init(struct asus_wmi *asus)
{
+ struct device *dev = &asus->platform_device->dev;
struct device *hwmon;
- hwmon = hwmon_device_register_with_groups(&asus->platform_device->dev,
- "asus", asus,
- hwmon_attribute_groups);
+ hwmon = devm_hwmon_device_register_with_groups(dev, "asus", asus,
+ hwmon_attribute_groups);
+
if (IS_ERR(hwmon)) {
pr_err("Could not register asus hwmon device\n");
return PTR_ERR(hwmon);
@@ -1427,9 +1466,137 @@ static int asus_wmi_hwmon_init(struct asus_wmi *asus)
return 0;
}
-/*
- * Backlight
- */
+static int asus_wmi_fan_init(struct asus_wmi *asus)
+{
+ int status;
+
+ asus->asus_hwmon_pwm = -1;
+ asus->asus_hwmon_num_fans = -1;
+ asus->asus_hwmon_fan_manual_mode = false;
+
+ status = asus_hwmon_get_fan_number(asus, &asus->asus_hwmon_num_fans);
+ if (status) {
+ asus->asus_hwmon_num_fans = 0;
+ pr_warn("Could not determine number of fans: %d\n", status);
+ return -ENXIO;
+ }
+
+ pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans);
+ return 0;
+}
+
+/* Fan mode *******************************************************************/
+
+static int fan_mode_check_present(struct asus_wmi *asus)
+{
+ u32 result;
+ int err;
+
+ asus->fan_mode_available = false;
+
+ err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_MODE, &result);
+ if (err) {
+ if (err == -ENODEV)
+ return 0;
+ else
+ return err;
+ }
+
+ if ((result & ASUS_WMI_DSTS_PRESENCE_BIT) &&
+ (result & ASUS_FAN_MODES_MASK)) {
+ asus->fan_mode_available = true;
+ asus->fan_mode_mask = result & ASUS_FAN_MODES_MASK;
+ }
+
+ return 0;
+}
+
+static int fan_mode_write(struct asus_wmi *asus)
+{
+ int err;
+ u8 value;
+ u32 retval;
+
+ value = asus->fan_mode;
+
+ pr_info("Set fan mode: %u\n", value);
+ err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_MODE, value, &retval);
+
+ if (err) {
+ pr_warn("Failed to set fan mode: %d\n", err);
+ return err;
+ }
+
+ if (retval != 1) {
+ pr_warn("Failed to set fan mode (retval): 0x%x\n", retval);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int fan_mode_switch_next(struct asus_wmi *asus)
+{
+ if (asus->fan_mode == ASUS_FAN_MODE_NORMAL) {
+ if (asus->fan_mode_mask & ASUS_FAN_MODE_OVERBOOST_MASK)
+ asus->fan_mode = ASUS_FAN_MODE_OVERBOOST;
+ else if (asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK)
+ asus->fan_mode = ASUS_FAN_MODE_SILENT;
+ } else if (asus->fan_mode == ASUS_FAN_MODE_OVERBOOST) {
+ if (asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK)
+ asus->fan_mode = ASUS_FAN_MODE_SILENT;
+ else
+ asus->fan_mode = ASUS_FAN_MODE_NORMAL;
+ } else {
+ asus->fan_mode = ASUS_FAN_MODE_NORMAL;
+ }
+
+ return fan_mode_write(asus);
+}
+
+static ssize_t fan_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", asus->fan_mode);
+}
+
+static ssize_t fan_mode_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int result;
+ u8 new_mode;
+
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ result = kstrtou8(buf, 10, &new_mode);
+ if (result < 0) {
+ pr_warn("Trying to store invalid value\n");
+ return result;
+ }
+
+ if (new_mode == ASUS_FAN_MODE_OVERBOOST) {
+ if (!(asus->fan_mode_mask & ASUS_FAN_MODE_OVERBOOST_MASK))
+ return -EINVAL;
+ } else if (new_mode == ASUS_FAN_MODE_SILENT) {
+ if (!(asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK))
+ return -EINVAL;
+ } else if (new_mode != ASUS_FAN_MODE_NORMAL) {
+ return -EINVAL;
+ }
+
+ asus->fan_mode = new_mode;
+ fan_mode_write(asus);
+
+ return result;
+}
+
+// Fan mode: 0 - normal, 1 - overboost, 2 - silent
+static DEVICE_ATTR_RW(fan_mode);
+
+/* Backlight ******************************************************************/
+
static int read_backlight_power(struct asus_wmi *asus)
{
int ret;
@@ -1611,6 +1778,8 @@ static int is_display_toggle(int code)
return 0;
}
+/* Fn-lock ********************************************************************/
+
static bool asus_wmi_has_fnlock_key(struct asus_wmi *asus)
{
u32 result;
@@ -1628,88 +1797,148 @@ static void asus_wmi_fnlock_update(struct asus_wmi *asus)
asus_wmi_set_devstate(ASUS_WMI_DEVID_FNLOCK, mode, NULL);
}
-static void asus_wmi_notify(u32 value, void *context)
+/* WMI events *****************************************************************/
+
+static int asus_wmi_get_event_code(u32 value)
{
- struct asus_wmi *asus = context;
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status;
int code;
- int orig_code;
- unsigned int key_value = 1;
- bool autorelease = 1;
status = wmi_get_event_data(value, &response);
- if (status != AE_OK) {
- pr_err("bad event status 0x%x\n", status);
- return;
+ if (ACPI_FAILURE(status)) {
+ pr_warn("Failed to get WMI notify code: %s\n",
+ acpi_format_exception(status));
+ return -EIO;
}
obj = (union acpi_object *)response.pointer;
- if (!obj || obj->type != ACPI_TYPE_INTEGER)
- goto exit;
+ if (obj && obj->type == ACPI_TYPE_INTEGER)
+ code = (int)(obj->integer.value & WMI_EVENT_MASK);
+ else
+ code = -EIO;
+
+ kfree(obj);
+ return code;
+}
+
+static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
+{
+ int orig_code;
+ unsigned int key_value = 1;
+ bool autorelease = 1;
- code = obj->integer.value;
orig_code = code;
if (asus->driver->key_filter) {
asus->driver->key_filter(asus->driver, &code, &key_value,
&autorelease);
if (code == ASUS_WMI_KEY_IGNORE)
- goto exit;
+ return;
}
if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
code = ASUS_WMI_BRN_UP;
- else if (code >= NOTIFY_BRNDOWN_MIN &&
- code <= NOTIFY_BRNDOWN_MAX)
+ else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)
code = ASUS_WMI_BRN_DOWN;
if (code == ASUS_WMI_BRN_DOWN || code == ASUS_WMI_BRN_UP) {
if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
asus_wmi_backlight_notify(asus, orig_code);
- goto exit;
+ return;
}
}
if (code == NOTIFY_KBD_BRTUP) {
kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
- goto exit;
+ return;
}
if (code == NOTIFY_KBD_BRTDWN) {
kbd_led_set_by_kbd(asus, asus->kbd_led_wk - 1);
- goto exit;
+ return;
}
if (code == NOTIFY_KBD_BRTTOGGLE) {
if (asus->kbd_led_wk == asus->kbd_led.max_brightness)
kbd_led_set_by_kbd(asus, 0);
else
kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
- goto exit;
+ return;
}
if (code == NOTIFY_FNLOCK_TOGGLE) {
asus->fnlock_locked = !asus->fnlock_locked;
asus_wmi_fnlock_update(asus);
- goto exit;
+ return;
}
- if (is_display_toggle(code) &&
- asus->driver->quirks->no_display_toggle)
- goto exit;
+ if (asus->fan_mode_available && code == NOTIFY_KBD_FBM) {
+ fan_mode_switch_next(asus);
+ return;
+ }
+
+ if (is_display_toggle(code) && asus->driver->quirks->no_display_toggle)
+ return;
if (!sparse_keymap_report_event(asus->inputdev, code,
key_value, autorelease))
pr_info("Unknown key %x pressed\n", code);
+}
-exit:
- kfree(obj);
+static void asus_wmi_notify(u32 value, void *context)
+{
+ struct asus_wmi *asus = context;
+ int code;
+ int i;
+
+ for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) {
+ code = asus_wmi_get_event_code(value);
+
+ if (code < 0) {
+ pr_warn("Failed to get notify code: %d\n", code);
+ return;
+ }
+
+ if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK)
+ return;
+
+ asus_wmi_handle_event_code(code, asus);
+
+ /*
+ * Double check that queue is present:
+ * ATK (with queue) uses 0xff, ASUSWMI (without) 0xd2.
+ */
+ if (!asus->wmi_event_queue || value != WMI_EVENT_VALUE_ATK)
+ return;
+ }
+
+ pr_warn("Failed to process event queue, last code: 0x%x\n", code);
}
-/*
- * Sys helpers
- */
+static int asus_wmi_notify_queue_flush(struct asus_wmi *asus)
+{
+ int code;
+ int i;
+
+ for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) {
+ code = asus_wmi_get_event_code(WMI_EVENT_VALUE_ATK);
+
+ if (code < 0) {
+ pr_warn("Failed to get event during flush: %d\n", code);
+ return code;
+ }
+
+ if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK)
+ return 0;
+ }
+
+ pr_warn("Failed to flush event queue\n");
+ return -EIO;
+}
+
+/* Sysfs **********************************************************************/
+
static int parse_arg(const char *buf, unsigned long count, int *val)
{
if (!count)
@@ -1805,6 +2034,7 @@ static struct attribute *platform_attributes[] = {
&dev_attr_touchpad.attr,
&dev_attr_lid_resume.attr,
&dev_attr_als_enable.attr,
+ &dev_attr_fan_mode.attr,
NULL
};
@@ -1826,6 +2056,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
devid = ASUS_WMI_DEVID_LID_RESUME;
else if (attr == &dev_attr_als_enable.attr)
devid = ASUS_WMI_DEVID_ALS_ENABLE;
+ else if (attr == &dev_attr_fan_mode.attr)
+ ok = asus->fan_mode_available;
if (devid != -1)
ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
@@ -1848,11 +2080,12 @@ static int asus_wmi_sysfs_init(struct platform_device *device)
return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
}
-/*
- * Platform device
- */
+/* Platform device ************************************************************/
+
static int asus_wmi_platform_init(struct asus_wmi *asus)
{
+ struct device *dev = &asus->platform_device->dev;
+ char *wmi_uid;
int rv;
/* INIT enable hotkeys on some models */
@@ -1882,11 +2115,41 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
* Note, on most Eeepc, there is no way to check if a method exist
* or note, while on notebooks, they returns 0xFFFFFFFE on failure,
* but once again, SPEC may probably be used for that kind of things.
+ *
+ * Additionally at least TUF Gaming series laptops return nothing for
+ * unknown methods, so the detection in this way is not possible.
+ *
+ * There is strong indication that only ACPI WMI devices that have _UID
+ * equal to "ASUSWMI" use DCTS whereas those with "ATK" use DSTS.
*/
- if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL))
+ wmi_uid = wmi_get_acpi_device_uid(ASUS_WMI_MGMT_GUID);
+ if (!wmi_uid)
+ return -ENODEV;
+
+ if (!strcmp(wmi_uid, ASUS_ACPI_UID_ASUSWMI)) {
+ dev_info(dev, "Detected ASUSWMI, use DCTS\n");
+ asus->dsts_id = ASUS_WMI_METHODID_DCTS;
+ } else {
+ dev_info(dev, "Detected %s, not ASUSWMI, use DSTS\n", wmi_uid);
asus->dsts_id = ASUS_WMI_METHODID_DSTS;
- else
- asus->dsts_id = ASUS_WMI_METHODID_DSTS2;
+ }
+
+ /*
+ * Some devices can have multiple event codes stored in a queue before
+ * the module load if it was unloaded intermittently after calling
+ * the INIT method (enables event handling). The WMI notify handler is
+ * expected to retrieve all event codes until a retrieved code equals
+ * queue end marker (One or Ones). Old codes are flushed from the queue
+ * upon module load. Not enabling this when it should be has minimal
+ * visible impact so fall back if anything goes wrong.
+ */
+ wmi_uid = wmi_get_acpi_device_uid(asus->driver->event_guid);
+ if (wmi_uid && !strcmp(wmi_uid, ASUS_ACPI_UID_ATK)) {
+ dev_info(dev, "Detected ATK, enable event queue\n");
+
+ if (!asus_wmi_notify_queue_flush(asus))
+ asus->wmi_event_queue = true;
+ }
/* CWAP allow to define the behavior of the Fn+F2 key,
* this method doesn't seems to be present on Eee PCs */
@@ -1894,17 +2157,11 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP,
asus->driver->quirks->wapf, NULL);
- return asus_wmi_sysfs_init(asus->platform_device);
+ return 0;
}
-static void asus_wmi_platform_exit(struct asus_wmi *asus)
-{
- asus_wmi_sysfs_exit(asus->platform_device);
-}
+/* debugfs ********************************************************************/
-/*
- * debugfs
- */
struct asus_wmi_debugfs_node {
struct asus_wmi *asus;
char *name;
@@ -2005,74 +2262,33 @@ static void asus_wmi_debugfs_exit(struct asus_wmi *asus)
debugfs_remove_recursive(asus->debug.root);
}
-static int asus_wmi_debugfs_init(struct asus_wmi *asus)
+static void asus_wmi_debugfs_init(struct asus_wmi *asus)
{
- struct dentry *dent;
int i;
asus->debug.root = debugfs_create_dir(asus->driver->name, NULL);
- if (!asus->debug.root) {
- pr_err("failed to create debugfs directory\n");
- goto error_debugfs;
- }
- dent = debugfs_create_x32("method_id", S_IRUGO | S_IWUSR,
- asus->debug.root, &asus->debug.method_id);
- if (!dent)
- goto error_debugfs;
+ debugfs_create_x32("method_id", S_IRUGO | S_IWUSR, asus->debug.root,
+ &asus->debug.method_id);
- dent = debugfs_create_x32("dev_id", S_IRUGO | S_IWUSR,
- asus->debug.root, &asus->debug.dev_id);
- if (!dent)
- goto error_debugfs;
+ debugfs_create_x32("dev_id", S_IRUGO | S_IWUSR, asus->debug.root,
+ &asus->debug.dev_id);
- dent = debugfs_create_x32("ctrl_param", S_IRUGO | S_IWUSR,
- asus->debug.root, &asus->debug.ctrl_param);
- if (!dent)
- goto error_debugfs;
+ debugfs_create_x32("ctrl_param", S_IRUGO | S_IWUSR, asus->debug.root,
+ &asus->debug.ctrl_param);
for (i = 0; i < ARRAY_SIZE(asus_wmi_debug_files); i++) {
struct asus_wmi_debugfs_node *node = &asus_wmi_debug_files[i];
node->asus = asus;
- dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO,
- asus->debug.root, node,
- &asus_wmi_debugfs_io_ops);
- if (!dent) {
- pr_err("failed to create debug file: %s\n", node->name);
- goto error_debugfs;
- }
+ debugfs_create_file(node->name, S_IFREG | S_IRUGO,
+ asus->debug.root, node,
+ &asus_wmi_debugfs_io_ops);
}
-
- return 0;
-
-error_debugfs:
- asus_wmi_debugfs_exit(asus);
- return -ENOMEM;
}
-static int asus_wmi_fan_init(struct asus_wmi *asus)
-{
- int status;
-
- asus->asus_hwmon_pwm = -1;
- asus->asus_hwmon_num_fans = -1;
- asus->asus_hwmon_fan_manual_mode = false;
-
- status = asus_hwmon_get_fan_number(asus, &asus->asus_hwmon_num_fans);
- if (status) {
- asus->asus_hwmon_num_fans = 0;
- pr_warn("Could not determine number of fans: %d\n", status);
- return -ENXIO;
- }
-
- pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans);
- return 0;
-}
+/* Init / exit ****************************************************************/
-/*
- * WMI Driver
- */
static int asus_wmi_add(struct platform_device *pdev)
{
struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver);
@@ -2099,6 +2315,14 @@ static int asus_wmi_add(struct platform_device *pdev)
if (err)
goto fail_platform;
+ err = fan_mode_check_present(asus);
+ if (err)
+ goto fail_fan_mode;
+
+ err = asus_wmi_sysfs_init(asus->platform_device);
+ if (err)
+ goto fail_sysfs;
+
err = asus_wmi_input_init(asus);
if (err)
goto fail_input;
@@ -2162,14 +2386,10 @@ static int asus_wmi_add(struct platform_device *pdev)
goto fail_wmi_handler;
}
- err = asus_wmi_debugfs_init(asus);
- if (err)
- goto fail_debugfs;
+ asus_wmi_debugfs_init(asus);
return 0;
-fail_debugfs:
- wmi_remove_notify_handler(asus->driver->event_guid);
fail_wmi_handler:
asus_wmi_backlight_exit(asus);
fail_backlight:
@@ -2180,7 +2400,9 @@ fail_leds:
fail_hwmon:
asus_wmi_input_exit(asus);
fail_input:
- asus_wmi_platform_exit(asus);
+ asus_wmi_sysfs_exit(asus->platform_device);
+fail_sysfs:
+fail_fan_mode:
fail_platform:
kfree(asus);
return err;
@@ -2197,16 +2419,15 @@ static int asus_wmi_remove(struct platform_device *device)
asus_wmi_led_exit(asus);
asus_wmi_rfkill_exit(asus);
asus_wmi_debugfs_exit(asus);
- asus_wmi_platform_exit(asus);
+ asus_wmi_sysfs_exit(asus->platform_device);
asus_hwmon_fan_set_auto(asus);
kfree(asus);
return 0;
}
-/*
- * Platform driver - hibernate/resume callbacks
- */
+/* Platform driver - hibernate/resume callbacks *******************************/
+
static int asus_hotk_thaw(struct device *device)
{
struct asus_wmi *asus = dev_get_drvdata(device);
@@ -2282,6 +2503,8 @@ static const struct dev_pm_ops asus_pm_ops = {
.resume = asus_hotk_resume,
};
+/* Registration ***************************************************************/
+
static int asus_wmi_probe(struct platform_device *pdev)
{
struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver);
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index 4e2f76aa98de..d27be2836bc2 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -2173,9 +2173,8 @@ static int __init dell_init(void)
kbd_led_init(&platform_device->dev);
dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
- if (dell_laptop_dir != NULL)
- debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
- &dell_debugfs_fops);
+ debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
+ &dell_debugfs_fops);
dell_laptop_register_notifier(&dell_laptop_notifier);
diff --git a/drivers/platform/x86/dell-smbios-wmi.c b/drivers/platform/x86/dell-smbios-wmi.c
index 942b5b77883a..27a298b7c541 100644
--- a/drivers/platform/x86/dell-smbios-wmi.c
+++ b/drivers/platform/x86/dell-smbios-wmi.c
@@ -143,7 +143,7 @@ fail_smbios_cmd:
return ret;
}
-static int dell_smbios_wmi_probe(struct wmi_device *wdev)
+static int dell_smbios_wmi_probe(struct wmi_device *wdev, const void *context)
{
struct wmi_driver *wdriver =
container_of(wdev->dev.driver, struct wmi_driver, driver);
diff --git a/drivers/platform/x86/dell-wmi-descriptor.c b/drivers/platform/x86/dell-wmi-descriptor.c
index f0df49e3f8c9..a068900ae8a1 100644
--- a/drivers/platform/x86/dell-wmi-descriptor.c
+++ b/drivers/platform/x86/dell-wmi-descriptor.c
@@ -98,7 +98,8 @@ EXPORT_SYMBOL_GPL(dell_wmi_get_hotfix);
* WMI buffer length 12 4 <length>
* WMI hotfix number 16 4 <hotfix>
*/
-static int dell_wmi_descriptor_probe(struct wmi_device *wdev)
+static int dell_wmi_descriptor_probe(struct wmi_device *wdev,
+ const void *context)
{
union acpi_object *obj = NULL;
struct descriptor_priv *priv;
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
index 1f565fb69098..acc653f9c16f 100644
--- a/drivers/platform/x86/dell-wmi.c
+++ b/drivers/platform/x86/dell-wmi.c
@@ -659,7 +659,7 @@ static int dell_wmi_events_set_enabled(bool enable)
return dell_smbios_error(ret);
}
-static int dell_wmi_probe(struct wmi_device *wdev)
+static int dell_wmi_probe(struct wmi_device *wdev, const void *context)
{
struct dell_wmi_priv *priv;
int ret;
diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c
index f61b8a176e20..7a2747455237 100644
--- a/drivers/platform/x86/hp_accel.c
+++ b/drivers/platform/x86/hp_accel.c
@@ -229,6 +229,7 @@ static const struct dmi_system_id lis3lv02d_dmi_ids[] = {
AXIS_DMI_MATCH("HPB440G3", "HP ProBook 440 G3", x_inverted_usd),
AXIS_DMI_MATCH("HPB440G4", "HP ProBook 440 G4", x_inverted),
AXIS_DMI_MATCH("HPB442x", "HP ProBook 442", xy_rotated_left),
+ AXIS_DMI_MATCH("HPB450G0", "HP ProBook 450 G0", x_inverted),
AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted),
AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap),
AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted),
diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c
index 52fcac5b393a..195a7f3638cb 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -166,7 +166,7 @@ static int huawei_wmi_input_setup(struct wmi_device *wdev)
return input_register_device(priv->idev);
}
-static int huawei_wmi_probe(struct wmi_device *wdev)
+static int huawei_wmi_probe(struct wmi_device *wdev, const void *context)
{
struct huawei_wmi_priv *priv;
int err;
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index 5fb9bfdf1019..7598cd46cf60 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -316,34 +316,15 @@ static int debugfs_cfg_show(struct seq_file *s, void *data)
}
DEFINE_SHOW_ATTRIBUTE(debugfs_cfg);
-static int ideapad_debugfs_init(struct ideapad_private *priv)
+static void ideapad_debugfs_init(struct ideapad_private *priv)
{
- struct dentry *node;
+ struct dentry *dir;
- priv->debug = debugfs_create_dir("ideapad", NULL);
- if (priv->debug == NULL) {
- pr_err("failed to create debugfs directory");
- goto errout;
- }
-
- node = debugfs_create_file("cfg", S_IRUGO, priv->debug, priv,
- &debugfs_cfg_fops);
- if (!node) {
- pr_err("failed to create cfg in debugfs");
- goto errout;
- }
-
- node = debugfs_create_file("status", S_IRUGO, priv->debug, priv,
- &debugfs_status_fops);
- if (!node) {
- pr_err("failed to create status in debugfs");
- goto errout;
- }
-
- return 0;
+ dir = debugfs_create_dir("ideapad", NULL);
+ priv->debug = dir;
-errout:
- return -ENOMEM;
+ debugfs_create_file("cfg", S_IRUGO, dir, priv, &debugfs_cfg_fops);
+ debugfs_create_file("status", S_IRUGO, dir, priv, &debugfs_status_fops);
}
static void ideapad_debugfs_exit(struct ideapad_private *priv)
@@ -1012,9 +993,7 @@ static int ideapad_acpi_add(struct platform_device *pdev)
if (ret)
return ret;
- ret = ideapad_debugfs_init(priv);
- if (ret)
- goto debugfs_failed;
+ ideapad_debugfs_init(priv);
ret = ideapad_input_init(priv);
if (ret)
@@ -1071,7 +1050,6 @@ backlight_failed:
ideapad_input_exit(priv);
input_failed:
ideapad_debugfs_exit(priv);
-debugfs_failed:
ideapad_sysfs_exit(priv);
return ret;
}
diff --git a/drivers/platform/x86/intel-wmi-thunderbolt.c b/drivers/platform/x86/intel-wmi-thunderbolt.c
index 4dfa61434a76..974c22a7ff61 100644
--- a/drivers/platform/x86/intel-wmi-thunderbolt.c
+++ b/drivers/platform/x86/intel-wmi-thunderbolt.c
@@ -56,7 +56,8 @@ static const struct attribute_group tbt_attribute_group = {
.attrs = tbt_attrs,
};
-static int intel_wmi_thunderbolt_probe(struct wmi_device *wdev)
+static int intel_wmi_thunderbolt_probe(struct wmi_device *wdev,
+ const void *context)
{
int ret;
diff --git a/drivers/platform/x86/intel_int0002_vgpio.c b/drivers/platform/x86/intel_int0002_vgpio.c
index 1694a9aec77c..d9542c661ddc 100644
--- a/drivers/platform/x86/intel_int0002_vgpio.c
+++ b/drivers/platform/x86/intel_int0002_vgpio.c
@@ -51,17 +51,6 @@
#define GPE0A_STS_PORT 0x420
#define GPE0A_EN_PORT 0x428
-#define BAYTRAIL 0x01
-#define CHERRYTRAIL 0x02
-
-#define ICPU(model, data) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, data }
-
-static const struct x86_cpu_id int0002_cpu_ids[] = {
- ICPU(INTEL_FAM6_ATOM_SILVERMONT, BAYTRAIL), /* Valleyview, Bay Trail */
- ICPU(INTEL_FAM6_ATOM_AIRMONT, CHERRYTRAIL), /* Braswell, Cherry Trail */
- {}
-};
-
/*
* As this is not a real GPIO at all, but just a hack to model an event in
* ACPI the get / set functions are dummy functions.
@@ -157,6 +146,12 @@ static struct irq_chip int0002_cht_irqchip = {
*/
};
+static const struct x86_cpu_id int0002_cpu_ids[] = {
+ INTEL_CPU_FAM6(ATOM_SILVERMONT, int0002_byt_irqchip), /* Valleyview, Bay Trail */
+ INTEL_CPU_FAM6(ATOM_AIRMONT, int0002_cht_irqchip), /* Braswell, Cherry Trail */
+ {}
+};
+
static int int0002_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -210,10 +205,7 @@ static int int0002_probe(struct platform_device *pdev)
return ret;
}
- if (cpu_id->driver_data == BAYTRAIL)
- irq_chip = &int0002_byt_irqchip;
- else
- irq_chip = &int0002_cht_irqchip;
+ irq_chip = (struct irq_chip *)cpu_id->driver_data;
ret = gpiochip_irqchip_add(chip, irq_chip, 0, handle_edge_irq,
IRQ_TYPE_NONE);
diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c
index 77eb8709c931..b102f6dd5693 100644
--- a/drivers/platform/x86/intel_menlow.c
+++ b/drivers/platform/x86/intel_menlow.c
@@ -180,9 +180,13 @@ static int intel_menlow_memory_add(struct acpi_device *device)
static int intel_menlow_memory_remove(struct acpi_device *device)
{
- struct thermal_cooling_device *cdev = acpi_driver_data(device);
+ struct thermal_cooling_device *cdev;
+
+ if (!device)
+ return -EINVAL;
- if (!device || !cdev)
+ cdev = acpi_driver_data(device);
+ if (!cdev)
return -EINVAL;
sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c
index 1d902230ba61..235c0b89f824 100644
--- a/drivers/platform/x86/intel_pmc_core.c
+++ b/drivers/platform/x86/intel_pmc_core.c
@@ -26,6 +26,7 @@
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
#include <asm/msr.h>
+#include <asm/tsc.h>
#include "intel_pmc_core.h"
@@ -740,7 +741,9 @@ static int pmc_core_pkgc_show(struct seq_file *s, void *unused)
if (rdmsrl_safe(map[index].bit_mask, &pcstate_count))
continue;
- seq_printf(s, "%-8s : 0x%llx\n", map[index].name,
+ pcstate_count *= 1000;
+ do_div(pcstate_count, tsc_khz);
+ seq_printf(s, "%-8s : %llu\n", map[index].name,
pcstate_count);
}
@@ -753,14 +756,11 @@ static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
debugfs_remove_recursive(pmcdev->dbgfs_dir);
}
-static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
+static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
{
struct dentry *dir;
dir = debugfs_create_dir("pmc_core", NULL);
- if (!dir)
- return -ENOMEM;
-
pmcdev->dbgfs_dir = dir;
debugfs_create_file("slp_s0_residency_usec", 0444, dir, pmcdev,
@@ -794,13 +794,10 @@ static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
debugfs_create_bool("slp_s0_dbg_latch", 0644,
dir, &slps0_dbg_latch);
}
-
- return 0;
}
#else
-static inline int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
+static inline void pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
{
- return 0;
}
static inline void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
@@ -862,7 +859,6 @@ static int pmc_core_probe(struct platform_device *pdev)
struct pmc_dev *pmcdev = &pmc;
const struct x86_cpu_id *cpu_id;
u64 slp_s0_addr;
- int err;
if (device_initialized)
return -ENODEV;
@@ -896,12 +892,7 @@ static int pmc_core_probe(struct platform_device *pdev)
pmcdev->pmc_xram_read_bit = pmc_core_check_read_lock_bit();
dmi_check_system(pmc_core_dmi_table);
- err = pmc_core_dbgfs_register(pmcdev);
- if (err < 0) {
- dev_warn(&pdev->dev, "debugfs register failed.\n");
- iounmap(pmcdev->regbase);
- return err;
- }
+ pmc_core_dbgfs_register(pmcdev);
device_initialized = true;
dev_info(&pdev->dev, " initialized\n");
@@ -1023,47 +1014,23 @@ static const struct dev_pm_ops pmc_core_pm_ops = {
SET_LATE_SYSTEM_SLEEP_PM_OPS(pmc_core_suspend, pmc_core_resume)
};
+static const struct acpi_device_id pmc_core_acpi_ids[] = {
+ {"INT33A1", 0}, /* _HID for Intel Power Engine, _CID PNP0D80*/
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, pmc_core_acpi_ids);
+
static struct platform_driver pmc_core_driver = {
.driver = {
.name = "intel_pmc_core",
+ .acpi_match_table = ACPI_PTR(pmc_core_acpi_ids),
.pm = &pmc_core_pm_ops,
},
.probe = pmc_core_probe,
.remove = pmc_core_remove,
};
-static struct platform_device pmc_core_device = {
- .name = "intel_pmc_core",
-};
-
-static int __init pmc_core_init(void)
-{
- int ret;
-
- if (!x86_match_cpu(intel_pmc_core_ids))
- return -ENODEV;
-
- ret = platform_driver_register(&pmc_core_driver);
- if (ret)
- return ret;
-
- ret = platform_device_register(&pmc_core_device);
- if (ret) {
- platform_driver_unregister(&pmc_core_driver);
- return ret;
- }
-
- return 0;
-}
-
-static void __exit pmc_core_exit(void)
-{
- platform_device_unregister(&pmc_core_device);
- platform_driver_unregister(&pmc_core_driver);
-}
-
-module_init(pmc_core_init)
-module_exit(pmc_core_exit)
+module_platform_driver(pmc_core_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel PMC Core Driver");
diff --git a/drivers/platform/x86/intel_pmc_core_pltdrv.c b/drivers/platform/x86/intel_pmc_core_pltdrv.c
new file mode 100644
index 000000000000..a8754a6db1b8
--- /dev/null
+++ b/drivers/platform/x86/intel_pmc_core_pltdrv.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Intel PMC Core platform init
+ * Copyright (c) 2019, Google Inc.
+ * Author - Rajat Jain
+ *
+ * This code instantiates platform devices for intel_pmc_core driver, only
+ * on supported platforms that may not have the ACPI devices in the ACPI tables.
+ * No new platforms should be added here, because we expect that new platforms
+ * should all have the ACPI device, which is the preferred way of enumeration.
+ */
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/cpu_device_id.h>
+#include <asm/intel-family.h>
+
+static struct platform_device pmc_core_device = {
+ .name = "intel_pmc_core",
+};
+
+/*
+ * intel_pmc_core_platform_ids is the list of platforms where we want to
+ * instantiate the platform_device if not already instantiated. This is
+ * different than intel_pmc_core_ids in intel_pmc_core.c which is the
+ * list of platforms that the driver supports for pmc_core device. The
+ * other list may grow, but this list should not.
+ */
+static const struct x86_cpu_id intel_pmc_core_platform_ids[] = {
+ INTEL_CPU_FAM6(SKYLAKE_MOBILE, pmc_core_device),
+ INTEL_CPU_FAM6(SKYLAKE_DESKTOP, pmc_core_device),
+ INTEL_CPU_FAM6(KABYLAKE_MOBILE, pmc_core_device),
+ INTEL_CPU_FAM6(KABYLAKE_DESKTOP, pmc_core_device),
+ INTEL_CPU_FAM6(CANNONLAKE_MOBILE, pmc_core_device),
+ INTEL_CPU_FAM6(ICELAKE_MOBILE, pmc_core_device),
+ {}
+};
+MODULE_DEVICE_TABLE(x86cpu, intel_pmc_core_platform_ids);
+
+static int __init pmc_core_platform_init(void)
+{
+ /* Skip creating the platform device if ACPI already has a device */
+ if (acpi_dev_present("INT33A1", NULL, -1))
+ return -ENODEV;
+
+ if (!x86_match_cpu(intel_pmc_core_platform_ids))
+ return -ENODEV;
+
+ return platform_device_register(&pmc_core_device);
+}
+
+static void __exit pmc_core_platform_exit(void)
+{
+ platform_device_unregister(&pmc_core_device);
+}
+
+module_init(pmc_core_platform_init);
+module_exit(pmc_core_platform_exit);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/x86/intel_speed_select_if/Kconfig b/drivers/platform/x86/intel_speed_select_if/Kconfig
new file mode 100644
index 000000000000..ce3e3dc076d2
--- /dev/null
+++ b/drivers/platform/x86/intel_speed_select_if/Kconfig
@@ -0,0 +1,17 @@
+menu "Intel Speed Select Technology interface support"
+ depends on PCI
+ depends on X86_64 || COMPILE_TEST
+
+config INTEL_SPEED_SELECT_INTERFACE
+ tristate "Intel(R) Speed Select Technology interface drivers"
+ help
+ This config enables the Intel(R) Speed Select Technology interface
+ drivers. The Intel(R) speed select technology features are non
+ architectural and only supported on specific Xeon(R) servers.
+ These drivers provide interface to directly communicate with hardware
+ via MMIO and Mail boxes to enumerate and control all the speed select
+ features.
+
+ Enable this config, if there is a need to enable and control the
+ Intel(R) Speed Select Technology features from the user space.
+endmenu
diff --git a/drivers/platform/x86/intel_speed_select_if/Makefile b/drivers/platform/x86/intel_speed_select_if/Makefile
new file mode 100644
index 000000000000..856076206f35
--- /dev/null
+++ b/drivers/platform/x86/intel_speed_select_if/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile - Intel Speed Select Interface drivers
+# Copyright (c) 2019, Intel Corporation.
+#
+
+obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_common.o
+obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mmio.o
+obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_pci.o
+obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_msr.o
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_common.c b/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
new file mode 100644
index 000000000000..68d75391db57
--- /dev/null
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
@@ -0,0 +1,672 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Speed Select Interface: Common functions
+ * Copyright (c) 2019, Intel Corporation.
+ * All rights reserved.
+ *
+ * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+ */
+
+#include <linux/cpufeature.h>
+#include <linux/cpuhotplug.h>
+#include <linux/fs.h>
+#include <linux/hashtable.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/isst_if.h>
+
+#include "isst_if_common.h"
+
+#define MSR_THREAD_ID_INFO 0x53
+#define MSR_CPU_BUS_NUMBER 0x128
+
+static struct isst_if_cmd_cb punit_callbacks[ISST_IF_DEV_MAX];
+
+static int punit_msr_white_list[] = {
+ MSR_TURBO_RATIO_LIMIT,
+ MSR_CONFIG_TDP_CONTROL,
+};
+
+struct isst_valid_cmd_ranges {
+ u16 cmd;
+ u16 sub_cmd_beg;
+ u16 sub_cmd_end;
+};
+
+struct isst_cmd_set_req_type {
+ u16 cmd;
+ u16 sub_cmd;
+ u16 param;
+};
+
+static const struct isst_valid_cmd_ranges isst_valid_cmds[] = {
+ {0xD0, 0x00, 0x03},
+ {0x7F, 0x00, 0x0B},
+ {0x7F, 0x10, 0x12},
+ {0x7F, 0x20, 0x23},
+};
+
+static const struct isst_cmd_set_req_type isst_cmd_set_reqs[] = {
+ {0xD0, 0x00, 0x08},
+ {0xD0, 0x01, 0x08},
+ {0xD0, 0x02, 0x08},
+ {0xD0, 0x03, 0x08},
+ {0x7F, 0x02, 0x00},
+ {0x7F, 0x08, 0x00},
+};
+
+struct isst_cmd {
+ struct hlist_node hnode;
+ u64 data;
+ u32 cmd;
+ int cpu;
+ int mbox_cmd_type;
+ u32 param;
+};
+
+static DECLARE_HASHTABLE(isst_hash, 8);
+static DEFINE_MUTEX(isst_hash_lock);
+
+static int isst_store_new_cmd(int cmd, u32 cpu, int mbox_cmd_type, u32 param,
+ u32 data)
+{
+ struct isst_cmd *sst_cmd;
+
+ sst_cmd = kmalloc(sizeof(*sst_cmd), GFP_KERNEL);
+ if (!sst_cmd)
+ return -ENOMEM;
+
+ sst_cmd->cpu = cpu;
+ sst_cmd->cmd = cmd;
+ sst_cmd->mbox_cmd_type = mbox_cmd_type;
+ sst_cmd->param = param;
+ sst_cmd->data = data;
+
+ hash_add(isst_hash, &sst_cmd->hnode, sst_cmd->cmd);
+
+ return 0;
+}
+
+static void isst_delete_hash(void)
+{
+ struct isst_cmd *sst_cmd;
+ struct hlist_node *tmp;
+ int i;
+
+ hash_for_each_safe(isst_hash, i, tmp, sst_cmd, hnode) {
+ hash_del(&sst_cmd->hnode);
+ kfree(sst_cmd);
+ }
+}
+
+/**
+ * isst_store_cmd() - Store command to a hash table
+ * @cmd: Mailbox command.
+ * @sub_cmd: Mailbox sub-command or MSR id.
+ * @mbox_cmd_type: Mailbox or MSR command.
+ * @param: Mailbox parameter.
+ * @data: Mailbox request data or MSR data.
+ *
+ * Stores the command to a hash table if there is no such command already
+ * stored. If already stored update the latest parameter and data for the
+ * command.
+ *
+ * Return: Return result of store to hash table, 0 for success, others for
+ * failure.
+ */
+int isst_store_cmd(int cmd, int sub_cmd, u32 cpu, int mbox_cmd_type,
+ u32 param, u64 data)
+{
+ struct isst_cmd *sst_cmd;
+ int full_cmd, ret;
+
+ full_cmd = (cmd & GENMASK_ULL(15, 0)) << 16;
+ full_cmd |= (sub_cmd & GENMASK_ULL(15, 0));
+ mutex_lock(&isst_hash_lock);
+ hash_for_each_possible(isst_hash, sst_cmd, hnode, full_cmd) {
+ if (sst_cmd->cmd == full_cmd && sst_cmd->cpu == cpu &&
+ sst_cmd->mbox_cmd_type == mbox_cmd_type) {
+ sst_cmd->param = param;
+ sst_cmd->data = data;
+ mutex_unlock(&isst_hash_lock);
+ return 0;
+ }
+ }
+
+ ret = isst_store_new_cmd(full_cmd, cpu, mbox_cmd_type, param, data);
+ mutex_unlock(&isst_hash_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(isst_store_cmd);
+
+static void isst_mbox_resume_command(struct isst_if_cmd_cb *cb,
+ struct isst_cmd *sst_cmd)
+{
+ struct isst_if_mbox_cmd mbox_cmd;
+ int wr_only;
+
+ mbox_cmd.command = (sst_cmd->cmd & GENMASK_ULL(31, 16)) >> 16;
+ mbox_cmd.sub_command = sst_cmd->cmd & GENMASK_ULL(15, 0);
+ mbox_cmd.parameter = sst_cmd->param;
+ mbox_cmd.req_data = sst_cmd->data;
+ mbox_cmd.logical_cpu = sst_cmd->cpu;
+ (cb->cmd_callback)((u8 *)&mbox_cmd, &wr_only, 1);
+}
+
+/**
+ * isst_resume_common() - Process Resume request
+ *
+ * On resume replay all mailbox commands and MSRs.
+ *
+ * Return: None.
+ */
+void isst_resume_common(void)
+{
+ struct isst_cmd *sst_cmd;
+ int i;
+
+ hash_for_each(isst_hash, i, sst_cmd, hnode) {
+ struct isst_if_cmd_cb *cb;
+
+ if (sst_cmd->mbox_cmd_type) {
+ cb = &punit_callbacks[ISST_IF_DEV_MBOX];
+ if (cb->registered)
+ isst_mbox_resume_command(cb, sst_cmd);
+ } else {
+ wrmsrl_safe_on_cpu(sst_cmd->cpu, sst_cmd->cmd,
+ sst_cmd->data);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(isst_resume_common);
+
+static void isst_restore_msr_local(int cpu)
+{
+ struct isst_cmd *sst_cmd;
+ int i;
+
+ mutex_lock(&isst_hash_lock);
+ for (i = 0; i < ARRAY_SIZE(punit_msr_white_list); ++i) {
+ if (!punit_msr_white_list[i])
+ break;
+
+ hash_for_each_possible(isst_hash, sst_cmd, hnode,
+ punit_msr_white_list[i]) {
+ if (!sst_cmd->mbox_cmd_type && sst_cmd->cpu == cpu)
+ wrmsrl_safe(sst_cmd->cmd, sst_cmd->data);
+ }
+ }
+ mutex_unlock(&isst_hash_lock);
+}
+
+/**
+ * isst_if_mbox_cmd_invalid() - Check invalid mailbox commands
+ * @cmd: Pointer to the command structure to verify.
+ *
+ * Invalid command to PUNIT to may result in instability of the platform.
+ * This function has a whitelist of commands, which are allowed.
+ *
+ * Return: Return true if the command is invalid, else false.
+ */
+bool isst_if_mbox_cmd_invalid(struct isst_if_mbox_cmd *cmd)
+{
+ int i;
+
+ if (cmd->logical_cpu >= nr_cpu_ids)
+ return true;
+
+ for (i = 0; i < ARRAY_SIZE(isst_valid_cmds); ++i) {
+ if (cmd->command == isst_valid_cmds[i].cmd &&
+ (cmd->sub_command >= isst_valid_cmds[i].sub_cmd_beg &&
+ cmd->sub_command <= isst_valid_cmds[i].sub_cmd_end)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(isst_if_mbox_cmd_invalid);
+
+/**
+ * isst_if_mbox_cmd_set_req() - Check mailbox command is a set request
+ * @cmd: Pointer to the command structure to verify.
+ *
+ * Check if the given mail box level is set request and not a get request.
+ *
+ * Return: Return true if the command is set_req, else false.
+ */
+bool isst_if_mbox_cmd_set_req(struct isst_if_mbox_cmd *cmd)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(isst_cmd_set_reqs); ++i) {
+ if (cmd->command == isst_cmd_set_reqs[i].cmd &&
+ cmd->sub_command == isst_cmd_set_reqs[i].sub_cmd &&
+ cmd->parameter == isst_cmd_set_reqs[i].param) {
+ return true;
+ }
+ }
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(isst_if_mbox_cmd_set_req);
+
+static int isst_if_get_platform_info(void __user *argp)
+{
+ struct isst_if_platform_info info;
+
+ info.api_version = ISST_IF_API_VERSION,
+ info.driver_version = ISST_IF_DRIVER_VERSION,
+ info.max_cmds_per_ioctl = ISST_IF_CMD_LIMIT,
+ info.mbox_supported = punit_callbacks[ISST_IF_DEV_MBOX].registered;
+ info.mmio_supported = punit_callbacks[ISST_IF_DEV_MMIO].registered;
+
+ if (copy_to_user(argp, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+struct isst_if_cpu_info {
+ /* For BUS 0 and BUS 1 only, which we need for PUNIT interface */
+ int bus_info[2];
+ int punit_cpu_id;
+};
+
+static struct isst_if_cpu_info *isst_cpu_info;
+
+/**
+ * isst_if_get_pci_dev() - Get the PCI device instance for a CPU
+ * @cpu: Logical CPU number.
+ * @bus_number: The bus number assigned by the hardware.
+ * @dev: The device number assigned by the hardware.
+ * @fn: The function number assigned by the hardware.
+ *
+ * Using cached bus information, find out the PCI device for a bus number,
+ * device and function.
+ *
+ * Return: Return pci_dev pointer or NULL.
+ */
+struct pci_dev *isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn)
+{
+ int bus_number;
+
+ if (bus_no < 0 || bus_no > 1 || cpu < 0 || cpu >= nr_cpu_ids ||
+ cpu >= num_possible_cpus())
+ return NULL;
+
+ bus_number = isst_cpu_info[cpu].bus_info[bus_no];
+ if (bus_number < 0)
+ return NULL;
+
+ return pci_get_domain_bus_and_slot(0, bus_number, PCI_DEVFN(dev, fn));
+}
+EXPORT_SYMBOL_GPL(isst_if_get_pci_dev);
+
+static int isst_if_cpu_online(unsigned int cpu)
+{
+ u64 data;
+ int ret;
+
+ ret = rdmsrl_safe(MSR_CPU_BUS_NUMBER, &data);
+ if (ret) {
+ /* This is not a fatal error on MSR mailbox only I/F */
+ isst_cpu_info[cpu].bus_info[0] = -1;
+ isst_cpu_info[cpu].bus_info[1] = -1;
+ } else {
+ isst_cpu_info[cpu].bus_info[0] = data & 0xff;
+ isst_cpu_info[cpu].bus_info[1] = (data >> 8) & 0xff;
+ }
+
+ ret = rdmsrl_safe(MSR_THREAD_ID_INFO, &data);
+ if (ret) {
+ isst_cpu_info[cpu].punit_cpu_id = -1;
+ return ret;
+ }
+ isst_cpu_info[cpu].punit_cpu_id = data;
+
+ isst_restore_msr_local(cpu);
+
+ return 0;
+}
+
+static int isst_if_online_id;
+
+static int isst_if_cpu_info_init(void)
+{
+ int ret;
+
+ isst_cpu_info = kcalloc(num_possible_cpus(),
+ sizeof(*isst_cpu_info),
+ GFP_KERNEL);
+ if (!isst_cpu_info)
+ return -ENOMEM;
+
+ ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
+ "platform/x86/isst-if:online",
+ isst_if_cpu_online, NULL);
+ if (ret < 0) {
+ kfree(isst_cpu_info);
+ return ret;
+ }
+
+ isst_if_online_id = ret;
+
+ return 0;
+}
+
+static void isst_if_cpu_info_exit(void)
+{
+ cpuhp_remove_state(isst_if_online_id);
+ kfree(isst_cpu_info);
+};
+
+static long isst_if_proc_phyid_req(u8 *cmd_ptr, int *write_only, int resume)
+{
+ struct isst_if_cpu_map *cpu_map;
+
+ cpu_map = (struct isst_if_cpu_map *)cmd_ptr;
+ if (cpu_map->logical_cpu >= nr_cpu_ids ||
+ cpu_map->logical_cpu >= num_possible_cpus())
+ return -EINVAL;
+
+ *write_only = 0;
+ cpu_map->physical_cpu = isst_cpu_info[cpu_map->logical_cpu].punit_cpu_id;
+
+ return 0;
+}
+
+static bool match_punit_msr_white_list(int msr)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(punit_msr_white_list); ++i) {
+ if (punit_msr_white_list[i] == msr)
+ return true;
+ }
+
+ return false;
+}
+
+static long isst_if_msr_cmd_req(u8 *cmd_ptr, int *write_only, int resume)
+{
+ struct isst_if_msr_cmd *msr_cmd;
+ int ret;
+
+ msr_cmd = (struct isst_if_msr_cmd *)cmd_ptr;
+
+ if (!match_punit_msr_white_list(msr_cmd->msr))
+ return -EINVAL;
+
+ if (msr_cmd->logical_cpu >= nr_cpu_ids)
+ return -EINVAL;
+
+ if (msr_cmd->read_write) {
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ ret = wrmsrl_safe_on_cpu(msr_cmd->logical_cpu,
+ msr_cmd->msr,
+ msr_cmd->data);
+ *write_only = 1;
+ if (!ret && !resume)
+ ret = isst_store_cmd(0, msr_cmd->msr,
+ msr_cmd->logical_cpu,
+ 0, 0, msr_cmd->data);
+ } else {
+ u64 data;
+
+ ret = rdmsrl_safe_on_cpu(msr_cmd->logical_cpu,
+ msr_cmd->msr, &data);
+ if (!ret) {
+ msr_cmd->data = data;
+ *write_only = 0;
+ }
+ }
+
+
+ return ret;
+}
+
+static long isst_if_exec_multi_cmd(void __user *argp, struct isst_if_cmd_cb *cb)
+{
+ unsigned char __user *ptr;
+ u32 cmd_count;
+ u8 *cmd_ptr;
+ long ret;
+ int i;
+
+ /* Each multi command has u32 command count as the first field */
+ if (copy_from_user(&cmd_count, argp, sizeof(cmd_count)))
+ return -EFAULT;
+
+ if (!cmd_count || cmd_count > ISST_IF_CMD_LIMIT)
+ return -EINVAL;
+
+ cmd_ptr = kmalloc(cb->cmd_size, GFP_KERNEL);
+ if (!cmd_ptr)
+ return -ENOMEM;
+
+ /* cb->offset points to start of the command after the command count */
+ ptr = argp + cb->offset;
+
+ for (i = 0; i < cmd_count; ++i) {
+ int wr_only;
+
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ break;
+ }
+
+ if (copy_from_user(cmd_ptr, ptr, cb->cmd_size)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = cb->cmd_callback(cmd_ptr, &wr_only, 0);
+ if (ret)
+ break;
+
+ if (!wr_only && copy_to_user(ptr, cmd_ptr, cb->cmd_size)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ ptr += cb->cmd_size;
+ }
+
+ kfree(cmd_ptr);
+
+ return i ? i : ret;
+}
+
+static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ struct isst_if_cmd_cb cmd_cb;
+ struct isst_if_cmd_cb *cb;
+ long ret = -ENOTTY;
+
+ switch (cmd) {
+ case ISST_IF_GET_PLATFORM_INFO:
+ ret = isst_if_get_platform_info(argp);
+ break;
+ case ISST_IF_GET_PHY_ID:
+ cmd_cb.cmd_size = sizeof(struct isst_if_cpu_map);
+ cmd_cb.offset = offsetof(struct isst_if_cpu_maps, cpu_map);
+ cmd_cb.cmd_callback = isst_if_proc_phyid_req;
+ ret = isst_if_exec_multi_cmd(argp, &cmd_cb);
+ break;
+ case ISST_IF_IO_CMD:
+ cb = &punit_callbacks[ISST_IF_DEV_MMIO];
+ if (cb->registered)
+ ret = isst_if_exec_multi_cmd(argp, cb);
+ break;
+ case ISST_IF_MBOX_COMMAND:
+ cb = &punit_callbacks[ISST_IF_DEV_MBOX];
+ if (cb->registered)
+ ret = isst_if_exec_multi_cmd(argp, cb);
+ break;
+ case ISST_IF_MSR_COMMAND:
+ cmd_cb.cmd_size = sizeof(struct isst_if_msr_cmd);
+ cmd_cb.offset = offsetof(struct isst_if_msr_cmds, msr_cmd);
+ cmd_cb.cmd_callback = isst_if_msr_cmd_req;
+ ret = isst_if_exec_multi_cmd(argp, &cmd_cb);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static DEFINE_MUTEX(punit_misc_dev_lock);
+static int misc_usage_count;
+static int misc_device_ret;
+static int misc_device_open;
+
+static int isst_if_open(struct inode *inode, struct file *file)
+{
+ int i, ret = 0;
+
+ /* Fail open, if a module is going away */
+ mutex_lock(&punit_misc_dev_lock);
+ for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
+ struct isst_if_cmd_cb *cb = &punit_callbacks[i];
+
+ if (cb->registered && !try_module_get(cb->owner)) {
+ ret = -ENODEV;
+ break;
+ }
+ }
+ if (ret) {
+ int j;
+
+ for (j = 0; j < i; ++j) {
+ struct isst_if_cmd_cb *cb;
+
+ cb = &punit_callbacks[j];
+ if (cb->registered)
+ module_put(cb->owner);
+ }
+ } else {
+ misc_device_open++;
+ }
+ mutex_unlock(&punit_misc_dev_lock);
+
+ return ret;
+}
+
+static int isst_if_relase(struct inode *inode, struct file *f)
+{
+ int i;
+
+ mutex_lock(&punit_misc_dev_lock);
+ misc_device_open--;
+ for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
+ struct isst_if_cmd_cb *cb = &punit_callbacks[i];
+
+ if (cb->registered)
+ module_put(cb->owner);
+ }
+ mutex_unlock(&punit_misc_dev_lock);
+
+ return 0;
+}
+
+static const struct file_operations isst_if_char_driver_ops = {
+ .open = isst_if_open,
+ .unlocked_ioctl = isst_if_def_ioctl,
+ .release = isst_if_relase,
+};
+
+static struct miscdevice isst_if_char_driver = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "isst_interface",
+ .fops = &isst_if_char_driver_ops,
+};
+
+/**
+ * isst_if_cdev_register() - Register callback for IOCTL
+ * @device_type: The device type this callback handling.
+ * @cb: Callback structure.
+ *
+ * This function registers a callback to device type. On very first call
+ * it will register a misc device, which is used for user kernel interface.
+ * Other calls simply increment ref count. Registry will fail, if the user
+ * already opened misc device for operation. Also if the misc device
+ * creation failed, then it will not try again and all callers will get
+ * failure code.
+ *
+ * Return: Return the return value from the misc creation device or -EINVAL
+ * for unsupported device type.
+ */
+int isst_if_cdev_register(int device_type, struct isst_if_cmd_cb *cb)
+{
+ if (misc_device_ret)
+ return misc_device_ret;
+
+ if (device_type >= ISST_IF_DEV_MAX)
+ return -EINVAL;
+
+ mutex_lock(&punit_misc_dev_lock);
+ if (misc_device_open) {
+ mutex_unlock(&punit_misc_dev_lock);
+ return -EAGAIN;
+ }
+ if (!misc_usage_count) {
+ int ret;
+
+ misc_device_ret = misc_register(&isst_if_char_driver);
+ if (misc_device_ret)
+ goto unlock_exit;
+
+ ret = isst_if_cpu_info_init();
+ if (ret) {
+ misc_deregister(&isst_if_char_driver);
+ misc_device_ret = ret;
+ goto unlock_exit;
+ }
+ }
+ memcpy(&punit_callbacks[device_type], cb, sizeof(*cb));
+ punit_callbacks[device_type].registered = 1;
+ misc_usage_count++;
+unlock_exit:
+ mutex_unlock(&punit_misc_dev_lock);
+
+ return misc_device_ret;
+}
+EXPORT_SYMBOL_GPL(isst_if_cdev_register);
+
+/**
+ * isst_if_cdev_unregister() - Unregister callback for IOCTL
+ * @device_type: The device type to unregister.
+ *
+ * This function unregisters the previously registered callback. If this
+ * is the last callback unregistering, then misc device is removed.
+ *
+ * Return: None.
+ */
+void isst_if_cdev_unregister(int device_type)
+{
+ mutex_lock(&punit_misc_dev_lock);
+ misc_usage_count--;
+ punit_callbacks[device_type].registered = 0;
+ if (device_type == ISST_IF_DEV_MBOX)
+ isst_delete_hash();
+ if (!misc_usage_count && !misc_device_ret) {
+ misc_deregister(&isst_if_char_driver);
+ isst_if_cpu_info_exit();
+ }
+ mutex_unlock(&punit_misc_dev_lock);
+}
+EXPORT_SYMBOL_GPL(isst_if_cdev_unregister);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_common.h b/drivers/platform/x86/intel_speed_select_if/isst_if_common.h
new file mode 100644
index 000000000000..1409a5bb5582
--- /dev/null
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_common.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Intel Speed Select Interface: Drivers Internal defines
+ * Copyright (c) 2019, Intel Corporation.
+ * All rights reserved.
+ *
+ * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+ */
+
+#ifndef __ISST_IF_COMMON_H
+#define __ISST_IF_COMMON_H
+
+#define INTEL_RAPL_PRIO_DEVID_0 0x3451
+#define INTEL_CFG_MBOX_DEVID_0 0x3459
+
+/*
+ * Validate maximum commands in a single request.
+ * This is enough to handle command to every core in one ioctl, or all
+ * possible message id to one CPU. Limit is also helpful for resonse time
+ * per IOCTL request, as PUNIT may take different times to process each
+ * request and may hold for long for too many commands.
+ */
+#define ISST_IF_CMD_LIMIT 64
+
+#define ISST_IF_API_VERSION 0x01
+#define ISST_IF_DRIVER_VERSION 0x01
+
+#define ISST_IF_DEV_MBOX 0
+#define ISST_IF_DEV_MMIO 1
+#define ISST_IF_DEV_MAX 2
+
+/**
+ * struct isst_if_cmd_cb - Used to register a IOCTL handler
+ * @registered: Used by the common code to store registry. Caller don't
+ * to touch this field
+ * @cmd_size: The command size of the individual command in IOCTL
+ * @offset: Offset to the first valid member in command structure.
+ * This will be the offset of the start of the command
+ * after command count field
+ * @cmd_callback: Callback function to handle IOCTL. The callback has the
+ * command pointer with data for command. There is a pointer
+ * called write_only, which when set, will not copy the
+ * response to user ioctl buffer. The "resume" argument
+ * can be used to avoid storing the command for replay
+ * during system resume
+ *
+ * This structure is used to register an handler for IOCTL. To avoid
+ * code duplication common code handles all the IOCTL command read/write
+ * including handling multiple command in single IOCTL. The caller just
+ * need to execute a command via the registered callback.
+ */
+struct isst_if_cmd_cb {
+ int registered;
+ int cmd_size;
+ int offset;
+ struct module *owner;
+ long (*cmd_callback)(u8 *ptr, int *write_only, int resume);
+};
+
+/* Internal interface functions */
+int isst_if_cdev_register(int type, struct isst_if_cmd_cb *cb);
+void isst_if_cdev_unregister(int type);
+struct pci_dev *isst_if_get_pci_dev(int cpu, int bus, int dev, int fn);
+bool isst_if_mbox_cmd_set_req(struct isst_if_mbox_cmd *mbox_cmd);
+bool isst_if_mbox_cmd_invalid(struct isst_if_mbox_cmd *cmd);
+int isst_store_cmd(int cmd, int sub_command, u32 cpu, int mbox_cmd,
+ u32 param, u64 data);
+void isst_resume_common(void);
+#endif
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c b/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c
new file mode 100644
index 000000000000..89b042aecef3
--- /dev/null
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Speed Select Interface: Mbox via MSR Interface
+ * Copyright (c) 2019, Intel Corporation.
+ * All rights reserved.
+ *
+ * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+ */
+
+#include <linux/module.h>
+#include <linux/cpuhotplug.h>
+#include <linux/pci.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+#include <linux/topology.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/isst_if.h>
+#include <asm/cpu_device_id.h>
+#include <asm/intel-family.h>
+
+#include "isst_if_common.h"
+
+#define MSR_OS_MAILBOX_INTERFACE 0xB0
+#define MSR_OS_MAILBOX_DATA 0xB1
+#define MSR_OS_MAILBOX_BUSY_BIT 31
+
+/*
+ * Based on experiments count is never more than 1, as the MSR overhead
+ * is enough to finish the command. So here this is the worst case number.
+ */
+#define OS_MAILBOX_RETRY_COUNT 3
+
+static int isst_if_send_mbox_cmd(u8 command, u8 sub_command, u32 parameter,
+ u32 command_data, u32 *response_data)
+{
+ u32 retries;
+ u64 data;
+ int ret;
+
+ /* Poll for rb bit == 0 */
+ retries = OS_MAILBOX_RETRY_COUNT;
+ do {
+ rdmsrl(MSR_OS_MAILBOX_INTERFACE, data);
+ if (data & BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT)) {
+ ret = -EBUSY;
+ continue;
+ }
+ ret = 0;
+ break;
+ } while (--retries);
+
+ if (ret)
+ return ret;
+
+ /* Write DATA register */
+ wrmsrl(MSR_OS_MAILBOX_DATA, command_data);
+
+ /* Write command register */
+ data = BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT) |
+ (parameter & GENMASK_ULL(13, 0)) << 16 |
+ (sub_command << 8) |
+ command;
+ wrmsrl(MSR_OS_MAILBOX_INTERFACE, data);
+
+ /* Poll for rb bit == 0 */
+ retries = OS_MAILBOX_RETRY_COUNT;
+ do {
+ rdmsrl(MSR_OS_MAILBOX_INTERFACE, data);
+ if (data & BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT)) {
+ ret = -EBUSY;
+ continue;
+ }
+
+ if (data & 0xff)
+ return -ENXIO;
+
+ if (response_data) {
+ rdmsrl(MSR_OS_MAILBOX_DATA, data);
+ *response_data = data;
+ }
+ ret = 0;
+ break;
+ } while (--retries);
+
+ return ret;
+}
+
+struct msrl_action {
+ int err;
+ struct isst_if_mbox_cmd *mbox_cmd;
+};
+
+/* revisit, smp_call_function_single should be enough for atomic mailbox! */
+static void msrl_update_func(void *info)
+{
+ struct msrl_action *act = info;
+
+ act->err = isst_if_send_mbox_cmd(act->mbox_cmd->command,
+ act->mbox_cmd->sub_command,
+ act->mbox_cmd->parameter,
+ act->mbox_cmd->req_data,
+ &act->mbox_cmd->resp_data);
+}
+
+static long isst_if_mbox_proc_cmd(u8 *cmd_ptr, int *write_only, int resume)
+{
+ struct msrl_action action;
+ int ret;
+
+ action.mbox_cmd = (struct isst_if_mbox_cmd *)cmd_ptr;
+
+ if (isst_if_mbox_cmd_invalid(action.mbox_cmd))
+ return -EINVAL;
+
+ if (isst_if_mbox_cmd_set_req(action.mbox_cmd) &&
+ !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ /*
+ * To complete mailbox command, we need to access two MSRs.
+ * So we don't want race to complete a mailbox transcation.
+ * Here smp_call ensures that msrl_update_func() has no race
+ * and also with wait flag, wait for completion.
+ * smp_call_function_single is using get_cpu() and put_cpu().
+ */
+ ret = smp_call_function_single(action.mbox_cmd->logical_cpu,
+ msrl_update_func, &action, 1);
+ if (ret)
+ return ret;
+
+ if (!action.err && !resume && isst_if_mbox_cmd_set_req(action.mbox_cmd))
+ action.err = isst_store_cmd(action.mbox_cmd->command,
+ action.mbox_cmd->sub_command,
+ action.mbox_cmd->logical_cpu, 1,
+ action.mbox_cmd->parameter,
+ action.mbox_cmd->req_data);
+ *write_only = 0;
+
+ return action.err;
+}
+
+
+static int isst_pm_notify(struct notifier_block *nb,
+ unsigned long mode, void *_unused)
+{
+ switch (mode) {
+ case PM_POST_HIBERNATION:
+ case PM_POST_RESTORE:
+ case PM_POST_SUSPEND:
+ isst_resume_common();
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block isst_pm_nb = {
+ .notifier_call = isst_pm_notify,
+};
+
+#define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, }
+
+static const struct x86_cpu_id isst_if_cpu_ids[] = {
+ ICPU(INTEL_FAM6_SKYLAKE_X),
+ {}
+};
+MODULE_DEVICE_TABLE(x86cpu, isst_if_cpu_ids);
+
+static int __init isst_if_mbox_init(void)
+{
+ struct isst_if_cmd_cb cb;
+ const struct x86_cpu_id *id;
+ u64 data;
+ int ret;
+
+ id = x86_match_cpu(isst_if_cpu_ids);
+ if (!id)
+ return -ENODEV;
+
+ /* Check presence of mailbox MSRs */
+ ret = rdmsrl_safe(MSR_OS_MAILBOX_INTERFACE, &data);
+ if (ret)
+ return ret;
+
+ ret = rdmsrl_safe(MSR_OS_MAILBOX_DATA, &data);
+ if (ret)
+ return ret;
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cmd_size = sizeof(struct isst_if_mbox_cmd);
+ cb.offset = offsetof(struct isst_if_mbox_cmds, mbox_cmd);
+ cb.cmd_callback = isst_if_mbox_proc_cmd;
+ cb.owner = THIS_MODULE;
+ ret = isst_if_cdev_register(ISST_IF_DEV_MBOX, &cb);
+ if (ret)
+ return ret;
+
+ ret = register_pm_notifier(&isst_pm_nb);
+ if (ret)
+ isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
+
+ return ret;
+}
+module_init(isst_if_mbox_init)
+
+static void __exit isst_if_mbox_exit(void)
+{
+ unregister_pm_notifier(&isst_pm_nb);
+ isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
+}
+module_exit(isst_if_mbox_exit)
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel speed select interface mailbox driver");
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_pci.c b/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_pci.c
new file mode 100644
index 000000000000..de4169d0796b
--- /dev/null
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_pci.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Speed Select Interface: Mbox via PCI Interface
+ * Copyright (c) 2019, Intel Corporation.
+ * All rights reserved.
+ *
+ * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+ */
+
+#include <linux/cpufeature.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/sched/signal.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/isst_if.h>
+
+#include "isst_if_common.h"
+
+#define PUNIT_MAILBOX_DATA 0xA0
+#define PUNIT_MAILBOX_INTERFACE 0xA4
+#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.
+ */
+#define OS_MAILBOX_RETRY_COUNT 50
+
+struct isst_if_device {
+ struct mutex mutex;
+};
+
+static int isst_if_mbox_cmd(struct pci_dev *pdev,
+ struct isst_if_mbox_cmd *mbox_cmd)
+{
+ u32 retries, data;
+ int ret;
+
+ /* Poll for rb bit == 0 */
+ retries = OS_MAILBOX_RETRY_COUNT;
+ do {
+ ret = pci_read_config_dword(pdev, PUNIT_MAILBOX_INTERFACE,
+ &data);
+ if (ret)
+ return ret;
+
+ if (data & BIT_ULL(PUNIT_MAILBOX_BUSY_BIT)) {
+ ret = -EBUSY;
+ continue;
+ }
+ ret = 0;
+ break;
+ } while (--retries);
+
+ if (ret)
+ return ret;
+
+ /* Write DATA register */
+ ret = pci_write_config_dword(pdev, PUNIT_MAILBOX_DATA,
+ mbox_cmd->req_data);
+ if (ret)
+ return ret;
+
+ /* Write command register */
+ data = BIT_ULL(PUNIT_MAILBOX_BUSY_BIT) |
+ (mbox_cmd->parameter & GENMASK_ULL(13, 0)) << 16 |
+ (mbox_cmd->sub_command << 8) |
+ mbox_cmd->command;
+
+ ret = pci_write_config_dword(pdev, PUNIT_MAILBOX_INTERFACE, data);
+ if (ret)
+ return ret;
+
+ /* Poll for rb bit == 0 */
+ retries = OS_MAILBOX_RETRY_COUNT;
+ do {
+ ret = pci_read_config_dword(pdev, PUNIT_MAILBOX_INTERFACE,
+ &data);
+ if (ret)
+ return ret;
+
+ if (data & BIT_ULL(PUNIT_MAILBOX_BUSY_BIT)) {
+ ret = -EBUSY;
+ continue;
+ }
+
+ if (data & 0xff)
+ return -ENXIO;
+
+ ret = pci_read_config_dword(pdev, PUNIT_MAILBOX_DATA, &data);
+ if (ret)
+ return ret;
+
+ mbox_cmd->resp_data = data;
+ ret = 0;
+ break;
+ } while (--retries);
+
+ return ret;
+}
+
+static long isst_if_mbox_proc_cmd(u8 *cmd_ptr, int *write_only, int resume)
+{
+ struct isst_if_mbox_cmd *mbox_cmd;
+ struct isst_if_device *punit_dev;
+ struct pci_dev *pdev;
+ int ret;
+
+ mbox_cmd = (struct isst_if_mbox_cmd *)cmd_ptr;
+
+ if (isst_if_mbox_cmd_invalid(mbox_cmd))
+ return -EINVAL;
+
+ if (isst_if_mbox_cmd_set_req(mbox_cmd) && !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ pdev = isst_if_get_pci_dev(mbox_cmd->logical_cpu, 1, 30, 1);
+ if (!pdev)
+ return -EINVAL;
+
+ punit_dev = pci_get_drvdata(pdev);
+ if (!punit_dev)
+ return -EINVAL;
+
+ /*
+ * Basically we are allowing one complete mailbox transaction on
+ * a mapped PCI device at a time.
+ */
+ mutex_lock(&punit_dev->mutex);
+ ret = isst_if_mbox_cmd(pdev, mbox_cmd);
+ if (!ret && !resume && isst_if_mbox_cmd_set_req(mbox_cmd))
+ ret = isst_store_cmd(mbox_cmd->command,
+ mbox_cmd->sub_command,
+ mbox_cmd->logical_cpu, 1,
+ mbox_cmd->parameter,
+ mbox_cmd->req_data);
+ mutex_unlock(&punit_dev->mutex);
+ if (ret)
+ return ret;
+
+ *write_only = 0;
+
+ return 0;
+}
+
+static const struct pci_device_id isst_if_mbox_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, INTEL_CFG_MBOX_DEVID_0)},
+ { 0 },
+};
+MODULE_DEVICE_TABLE(pci, isst_if_mbox_ids);
+
+static int isst_if_mbox_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct isst_if_device *punit_dev;
+ struct isst_if_cmd_cb cb;
+ int ret;
+
+ punit_dev = devm_kzalloc(&pdev->dev, sizeof(*punit_dev), GFP_KERNEL);
+ if (!punit_dev)
+ return -ENOMEM;
+
+ ret = pcim_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ mutex_init(&punit_dev->mutex);
+ pci_set_drvdata(pdev, punit_dev);
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cmd_size = sizeof(struct isst_if_mbox_cmd);
+ cb.offset = offsetof(struct isst_if_mbox_cmds, mbox_cmd);
+ cb.cmd_callback = isst_if_mbox_proc_cmd;
+ cb.owner = THIS_MODULE;
+ ret = isst_if_cdev_register(ISST_IF_DEV_MBOX, &cb);
+
+ if (ret)
+ mutex_destroy(&punit_dev->mutex);
+
+ return ret;
+}
+
+static void isst_if_mbox_remove(struct pci_dev *pdev)
+{
+ struct isst_if_device *punit_dev;
+
+ punit_dev = pci_get_drvdata(pdev);
+ isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
+ mutex_destroy(&punit_dev->mutex);
+}
+
+static int __maybe_unused isst_if_resume(struct device *device)
+{
+ isst_resume_common();
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(isst_if_pm_ops, NULL, isst_if_resume);
+
+static struct pci_driver isst_if_pci_driver = {
+ .name = "isst_if_mbox_pci",
+ .id_table = isst_if_mbox_ids,
+ .probe = isst_if_mbox_probe,
+ .remove = isst_if_mbox_remove,
+ .driver.pm = &isst_if_pm_ops,
+};
+
+module_pci_driver(isst_if_pci_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel speed select interface pci mailbox driver");
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c b/drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c
new file mode 100644
index 000000000000..f7266a115a08
--- /dev/null
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Speed Select Interface: MMIO Interface
+ * Copyright (c) 2019, Intel Corporation.
+ * All rights reserved.
+ *
+ * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/sched/signal.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/isst_if.h>
+
+#include "isst_if_common.h"
+
+struct isst_mmio_range {
+ int beg;
+ int end;
+};
+
+struct isst_mmio_range mmio_range[] = {
+ {0x04, 0x14},
+ {0x20, 0xD0},
+};
+
+struct isst_if_device {
+ void __iomem *punit_mmio;
+ u32 range_0[5];
+ u32 range_1[45];
+ struct mutex mutex;
+};
+
+static long isst_if_mmio_rd_wr(u8 *cmd_ptr, int *write_only, int resume)
+{
+ struct isst_if_device *punit_dev;
+ struct isst_if_io_reg *io_reg;
+ struct pci_dev *pdev;
+
+ io_reg = (struct isst_if_io_reg *)cmd_ptr;
+ if (io_reg->reg < 0x04 || io_reg->reg > 0xD0)
+ return -EINVAL;
+
+ if (io_reg->read_write && !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ pdev = isst_if_get_pci_dev(io_reg->logical_cpu, 0, 0, 1);
+ if (!pdev)
+ return -EINVAL;
+
+ punit_dev = pci_get_drvdata(pdev);
+ if (!punit_dev)
+ return -EINVAL;
+
+ /*
+ * Ensure that operation is complete on a PCI device to avoid read
+ * write race by using per PCI device mutex.
+ */
+ mutex_lock(&punit_dev->mutex);
+ if (io_reg->read_write) {
+ writel(io_reg->value, punit_dev->punit_mmio+io_reg->reg);
+ *write_only = 1;
+ } else {
+ io_reg->value = readl(punit_dev->punit_mmio+io_reg->reg);
+ *write_only = 0;
+ }
+ mutex_unlock(&punit_dev->mutex);
+
+ return 0;
+}
+
+static const struct pci_device_id isst_if_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, INTEL_RAPL_PRIO_DEVID_0)},
+ { 0 },
+};
+MODULE_DEVICE_TABLE(pci, isst_if_ids);
+
+static int isst_if_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct isst_if_device *punit_dev;
+ struct isst_if_cmd_cb cb;
+ u32 mmio_base, pcu_base;
+ u64 base_addr;
+ int ret;
+
+ punit_dev = devm_kzalloc(&pdev->dev, sizeof(*punit_dev), GFP_KERNEL);
+ if (!punit_dev)
+ return -ENOMEM;
+
+ ret = pcim_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ ret = pci_read_config_dword(pdev, 0xD0, &mmio_base);
+ if (ret)
+ return ret;
+
+ ret = pci_read_config_dword(pdev, 0xFC, &pcu_base);
+ if (ret)
+ return ret;
+
+ pcu_base &= GENMASK(10, 0);
+ base_addr = (u64)mmio_base << 23 | (u64) pcu_base << 12;
+ punit_dev->punit_mmio = devm_ioremap(&pdev->dev, base_addr, 256);
+ if (!punit_dev->punit_mmio)
+ return -ENOMEM;
+
+ mutex_init(&punit_dev->mutex);
+ pci_set_drvdata(pdev, punit_dev);
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cmd_size = sizeof(struct isst_if_io_reg);
+ cb.offset = offsetof(struct isst_if_io_regs, io_reg);
+ cb.cmd_callback = isst_if_mmio_rd_wr;
+ cb.owner = THIS_MODULE;
+ ret = isst_if_cdev_register(ISST_IF_DEV_MMIO, &cb);
+ if (ret)
+ mutex_destroy(&punit_dev->mutex);
+
+ return ret;
+}
+
+static void isst_if_remove(struct pci_dev *pdev)
+{
+ struct isst_if_device *punit_dev;
+
+ punit_dev = pci_get_drvdata(pdev);
+ isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
+ mutex_destroy(&punit_dev->mutex);
+}
+
+static int __maybe_unused isst_if_suspend(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct isst_if_device *punit_dev;
+ int i;
+
+ punit_dev = pci_get_drvdata(pdev);
+ for (i = 0; i < ARRAY_SIZE(punit_dev->range_0); ++i)
+ punit_dev->range_0[i] = readl(punit_dev->punit_mmio +
+ mmio_range[0].beg + 4 * i);
+ for (i = 0; i < ARRAY_SIZE(punit_dev->range_1); ++i)
+ punit_dev->range_1[i] = readl(punit_dev->punit_mmio +
+ mmio_range[1].beg + 4 * i);
+
+ return 0;
+}
+
+static int __maybe_unused isst_if_resume(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct isst_if_device *punit_dev;
+ int i;
+
+ punit_dev = pci_get_drvdata(pdev);
+ for (i = 0; i < ARRAY_SIZE(punit_dev->range_0); ++i)
+ writel(punit_dev->range_0[i], punit_dev->punit_mmio +
+ mmio_range[0].beg + 4 * i);
+ for (i = 0; i < ARRAY_SIZE(punit_dev->range_1); ++i)
+ writel(punit_dev->range_1[i], punit_dev->punit_mmio +
+ mmio_range[1].beg + 4 * i);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(isst_if_pm_ops, isst_if_suspend, isst_if_resume);
+
+static struct pci_driver isst_if_pci_driver = {
+ .name = "isst_if_pci",
+ .id_table = isst_if_ids,
+ .probe = isst_if_probe,
+ .remove = isst_if_remove,
+ .driver.pm = &isst_if_pm_ops,
+};
+
+module_pci_driver(isst_if_pci_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel speed select interface mmio driver");
diff --git a/drivers/platform/x86/intel_telemetry_debugfs.c b/drivers/platform/x86/intel_telemetry_debugfs.c
index 98ba9185a27b..e84d3e983e0c 100644
--- a/drivers/platform/x86/intel_telemetry_debugfs.c
+++ b/drivers/platform/x86/intel_telemetry_debugfs.c
@@ -900,7 +900,7 @@ static int __init telemetry_debugfs_init(void)
{
const struct x86_cpu_id *id;
int err;
- struct dentry *f;
+ struct dentry *dir;
/* Only APL supported for now */
id = x86_match_cpu(telemetry_debugfs_cpu_ids);
@@ -923,68 +923,22 @@ static int __init telemetry_debugfs_init(void)
register_pm_notifier(&pm_notifier);
- err = -ENOMEM;
- debugfs_conf->telemetry_dbg_dir = debugfs_create_dir("telemetry", NULL);
- if (!debugfs_conf->telemetry_dbg_dir)
- goto out_pm;
-
- f = debugfs_create_file("pss_info", S_IFREG | S_IRUGO,
- debugfs_conf->telemetry_dbg_dir, NULL,
- &telem_pss_states_fops);
- if (!f) {
- pr_err("pss_sample_info debugfs register failed\n");
- goto out;
- }
-
- f = debugfs_create_file("ioss_info", S_IFREG | S_IRUGO,
- debugfs_conf->telemetry_dbg_dir, NULL,
- &telem_ioss_states_fops);
- if (!f) {
- pr_err("ioss_sample_info debugfs register failed\n");
- goto out;
- }
-
- f = debugfs_create_file("soc_states", S_IFREG | S_IRUGO,
- debugfs_conf->telemetry_dbg_dir,
- NULL, &telem_soc_states_fops);
- if (!f) {
- pr_err("ioss_sample_info debugfs register failed\n");
- goto out;
- }
-
- f = debugfs_create_file("s0ix_residency_usec", S_IFREG | S_IRUGO,
- debugfs_conf->telemetry_dbg_dir,
- NULL, &telem_s0ix_fops);
- if (!f) {
- pr_err("s0ix_residency_usec debugfs register failed\n");
- goto out;
- }
-
- f = debugfs_create_file("pss_trace_verbosity", S_IFREG | S_IRUGO,
- debugfs_conf->telemetry_dbg_dir, NULL,
- &telem_pss_trc_verb_ops);
- if (!f) {
- pr_err("pss_trace_verbosity debugfs register failed\n");
- goto out;
- }
-
- f = debugfs_create_file("ioss_trace_verbosity", S_IFREG | S_IRUGO,
- debugfs_conf->telemetry_dbg_dir, NULL,
- &telem_ioss_trc_verb_ops);
- if (!f) {
- pr_err("ioss_trace_verbosity debugfs register failed\n");
- goto out;
- }
-
+ dir = debugfs_create_dir("telemetry", NULL);
+ debugfs_conf->telemetry_dbg_dir = dir;
+
+ debugfs_create_file("pss_info", S_IFREG | S_IRUGO, dir, NULL,
+ &telem_pss_states_fops);
+ debugfs_create_file("ioss_info", S_IFREG | S_IRUGO, dir, NULL,
+ &telem_ioss_states_fops);
+ debugfs_create_file("soc_states", S_IFREG | S_IRUGO, dir, NULL,
+ &telem_soc_states_fops);
+ debugfs_create_file("s0ix_residency_usec", S_IFREG | S_IRUGO, dir, NULL,
+ &telem_s0ix_fops);
+ debugfs_create_file("pss_trace_verbosity", S_IFREG | S_IRUGO, dir, NULL,
+ &telem_pss_trc_verb_ops);
+ debugfs_create_file("ioss_trace_verbosity", S_IFREG | S_IRUGO, dir,
+ NULL, &telem_ioss_trc_verb_ops);
return 0;
-
-out:
- debugfs_remove_recursive(debugfs_conf->telemetry_dbg_dir);
- debugfs_conf->telemetry_dbg_dir = NULL;
-out_pm:
- unregister_pm_notifier(&pm_notifier);
-
- return err;
}
static void __exit telemetry_debugfs_exit(void)
diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c
index 983f02b5b106..8fe51e43f1bc 100644
--- a/drivers/platform/x86/mlx-platform.c
+++ b/drivers/platform/x86/mlx-platform.c
@@ -44,6 +44,8 @@
#define MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET 0x3b
#define MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET 0x40
#define MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET 0x41
+#define MLXPLAT_CPLD_LPC_REG_AGGRCO_OFFSET 0x42
+#define MLXPLAT_CPLD_LPC_REG_AGGRCO_MASK_OFFSET 0x43
#define MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET 0x50
#define MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET 0x51
#define MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET 0x52
@@ -105,7 +107,9 @@
MLXPLAT_CPLD_AGGR_FAN_MASK_DEF)
#define MLXPLAT_CPLD_AGGR_ASIC_MASK_NG 0x01
#define MLXPLAT_CPLD_AGGR_MASK_NG_DEF 0x04
+#define MLXPLAT_CPLD_AGGR_MASK_COMEX BIT(0)
#define MLXPLAT_CPLD_LOW_AGGR_MASK_LOW 0xc1
+#define MLXPLAT_CPLD_LOW_AGGR_MASK_I2C BIT(6)
#define MLXPLAT_CPLD_PSU_MASK GENMASK(1, 0)
#define MLXPLAT_CPLD_PWR_MASK GENMASK(1, 0)
#define MLXPLAT_CPLD_FAN_MASK GENMASK(3, 0)
@@ -159,6 +163,7 @@
* @pdev_io_regs - register access platform devices
* @pdev_fan - FAN platform devices
* @pdev_wd - array of watchdog platform devices
+ * @regmap: device register map
*/
struct mlxplat_priv {
struct platform_device *pdev_i2c;
@@ -168,6 +173,7 @@ struct mlxplat_priv {
struct platform_device *pdev_io_regs;
struct platform_device *pdev_fan;
struct platform_device *pdev_wd[MLXPLAT_CPLD_WD_MAX_DEVS];
+ void *regmap;
};
/* Regions for LPC I2C controller and LPC base register space */
@@ -181,6 +187,14 @@ static const struct resource mlxplat_lpc_resources[] = {
IORESOURCE_IO),
};
+/* Platform next generation systems i2c data */
+static struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_i2c_ng_data = {
+ .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
+ .mask = MLXPLAT_CPLD_AGGR_MASK_COMEX,
+ .cell_low = MLXPLAT_CPLD_LPC_REG_AGGRCO_OFFSET,
+ .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_I2C,
+};
+
/* Platform default channels */
static const int mlxplat_default_channels[][MLXPLAT_CPLD_GRP_CHNL_NUM] = {
{
@@ -704,7 +718,7 @@ struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_default_ng_data = {
.items = mlxplat_mlxcpld_default_ng_items,
.counter = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_items),
.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
- .mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
+ .mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF | MLXPLAT_CPLD_AGGR_MASK_COMEX,
.cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET,
.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
};
@@ -1113,6 +1127,12 @@ static struct mlxreg_core_data mlxplat_mlxcpld_msn21xx_regs_io_data[] = {
.mode = 0444,
},
{
+ .label = "reset_sff_wd",
+ .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET,
+ .mask = GENMASK(7, 0) & ~BIT(6),
+ .mode = 0444,
+ },
+ {
.label = "psu1_on",
.reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(0),
@@ -1201,6 +1221,18 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = {
.mode = 0444,
},
{
+ .label = "reset_from_asic",
+ .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
+ .mask = GENMASK(7, 0) & ~BIT(5),
+ .mode = 0444,
+ },
+ {
+ .label = "reset_swb_wd",
+ .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
+ .mask = GENMASK(7, 0) & ~BIT(6),
+ .mode = 0444,
+ },
+ {
.label = "reset_asic_thermal",
.reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(7),
@@ -1213,6 +1245,12 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = {
.mode = 0444,
},
{
+ .label = "reset_comex_wd",
+ .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET,
+ .mask = GENMASK(7, 0) & ~BIT(6),
+ .mode = 0444,
+ },
+ {
.label = "reset_voltmon_upgrade_fail",
.reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(0),
@@ -1225,6 +1263,18 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = {
.mode = 0444,
},
{
+ .label = "reset_comex_thermal",
+ .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET,
+ .mask = GENMASK(7, 0) & ~BIT(3),
+ .mode = 0444,
+ },
+ {
+ .label = "reset_reload_bios",
+ .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET,
+ .mask = GENMASK(7, 0) & ~BIT(5),
+ .mode = 0444,
+ },
+ {
.label = "psu1_on",
.reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(0),
@@ -1531,6 +1581,7 @@ static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_WP2_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_AGGRCO_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
@@ -1578,6 +1629,8 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_AGGRCO_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_AGGRCO_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET:
@@ -1645,6 +1698,8 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_AGGRCO_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_AGGRCO_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET:
@@ -1691,6 +1746,11 @@ static const struct reg_default mlxplat_mlxcpld_regmap_default[] = {
{ MLXPLAT_CPLD_LPC_REG_WD_CLEAR_WP_OFFSET, 0x00 },
};
+static const struct reg_default mlxplat_mlxcpld_regmap_ng[] = {
+ { MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET, 0x00 },
+ { MLXPLAT_CPLD_LPC_REG_WD_CLEAR_WP_OFFSET, 0x00 },
+};
+
struct mlxplat_mlxcpld_regmap_context {
void __iomem *base;
};
@@ -1729,17 +1789,33 @@ static const struct regmap_config mlxplat_mlxcpld_regmap_config = {
.reg_write = mlxplat_mlxcpld_reg_write,
};
+static const struct regmap_config mlxplat_mlxcpld_regmap_config_ng = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 255,
+ .cache_type = REGCACHE_FLAT,
+ .writeable_reg = mlxplat_mlxcpld_writeable_reg,
+ .readable_reg = mlxplat_mlxcpld_readable_reg,
+ .volatile_reg = mlxplat_mlxcpld_volatile_reg,
+ .reg_defaults = mlxplat_mlxcpld_regmap_ng,
+ .num_reg_defaults = ARRAY_SIZE(mlxplat_mlxcpld_regmap_ng),
+ .reg_read = mlxplat_mlxcpld_reg_read,
+ .reg_write = mlxplat_mlxcpld_reg_write,
+};
+
static struct resource mlxplat_mlxcpld_resources[] = {
[0] = DEFINE_RES_IRQ_NAMED(17, "mlxreg-hotplug"),
};
static struct platform_device *mlxplat_dev;
+static struct mlxreg_core_hotplug_platform_data *mlxplat_i2c;
static struct mlxreg_core_hotplug_platform_data *mlxplat_hotplug;
static struct mlxreg_core_platform_data *mlxplat_led;
static struct mlxreg_core_platform_data *mlxplat_regs_io;
static struct mlxreg_core_platform_data *mlxplat_fan;
static struct mlxreg_core_platform_data
*mlxplat_wd_data[MLXPLAT_CPLD_WD_MAX_DEVS];
+static const struct regmap_config *mlxplat_regmap_config;
static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
{
@@ -1834,12 +1910,50 @@ static int __init mlxplat_dmi_qmb7xx_matched(const struct dmi_system_id *dmi)
mlxplat_fan = &mlxplat_default_fan_data;
for (i = 0; i < ARRAY_SIZE(mlxplat_mlxcpld_wd_set_type2); i++)
mlxplat_wd_data[i] = &mlxplat_mlxcpld_wd_set_type2[i];
+ mlxplat_i2c = &mlxplat_mlxcpld_i2c_ng_data;
+ mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_ng;
return 1;
};
static const struct dmi_system_id mlxplat_dmi_table[] __initconst = {
{
+ .callback = mlxplat_dmi_default_matched,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "VMOD0001"),
+ },
+ },
+ {
+ .callback = mlxplat_dmi_msn21xx_matched,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "VMOD0002"),
+ },
+ },
+ {
+ .callback = mlxplat_dmi_msn274x_matched,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "VMOD0003"),
+ },
+ },
+ {
+ .callback = mlxplat_dmi_msn201x_matched,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "VMOD0004"),
+ },
+ },
+ {
+ .callback = mlxplat_dmi_qmb7xx_matched,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "VMOD0005"),
+ },
+ },
+ {
+ .callback = mlxplat_dmi_qmb7xx_matched,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "VMOD0007"),
+ },
+ },
+ {
.callback = mlxplat_dmi_msn274x_matched,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
@@ -1916,42 +2030,6 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = {
DMI_MATCH(DMI_PRODUCT_NAME, "MSN38"),
},
},
- {
- .callback = mlxplat_dmi_default_matched,
- .matches = {
- DMI_MATCH(DMI_BOARD_NAME, "VMOD0001"),
- },
- },
- {
- .callback = mlxplat_dmi_msn21xx_matched,
- .matches = {
- DMI_MATCH(DMI_BOARD_NAME, "VMOD0002"),
- },
- },
- {
- .callback = mlxplat_dmi_msn274x_matched,
- .matches = {
- DMI_MATCH(DMI_BOARD_NAME, "VMOD0003"),
- },
- },
- {
- .callback = mlxplat_dmi_msn201x_matched,
- .matches = {
- DMI_MATCH(DMI_BOARD_NAME, "VMOD0004"),
- },
- },
- {
- .callback = mlxplat_dmi_qmb7xx_matched,
- .matches = {
- DMI_MATCH(DMI_BOARD_NAME, "VMOD0005"),
- },
- },
- {
- .callback = mlxplat_dmi_qmb7xx_matched,
- .matches = {
- DMI_MATCH(DMI_BOARD_NAME, "VMOD0007"),
- },
- },
{ }
};
@@ -2018,13 +2096,36 @@ static int __init mlxplat_init(void)
}
platform_set_drvdata(mlxplat_dev, priv);
+ mlxplat_mlxcpld_regmap_ctx.base = devm_ioport_map(&mlxplat_dev->dev,
+ mlxplat_lpc_resources[1].start, 1);
+ if (!mlxplat_mlxcpld_regmap_ctx.base) {
+ err = -ENOMEM;
+ goto fail_alloc;
+ }
+
+ if (!mlxplat_regmap_config)
+ mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config;
+
+ priv->regmap = devm_regmap_init(&mlxplat_dev->dev, NULL,
+ &mlxplat_mlxcpld_regmap_ctx,
+ mlxplat_regmap_config);
+ if (IS_ERR(priv->regmap)) {
+ err = PTR_ERR(priv->regmap);
+ goto fail_alloc;
+ }
+
err = mlxplat_mlxcpld_verify_bus_topology(&nr);
if (nr < 0)
goto fail_alloc;
nr = (nr == MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM) ? -1 : nr;
- priv->pdev_i2c = platform_device_register_simple("i2c_mlxcpld", nr,
- NULL, 0);
+ if (mlxplat_i2c)
+ mlxplat_i2c->regmap = priv->regmap;
+ priv->pdev_i2c = platform_device_register_resndata(
+ &mlxplat_dev->dev, "i2c_mlxcpld",
+ nr, mlxplat_mlxcpld_resources,
+ ARRAY_SIZE(mlxplat_mlxcpld_resources),
+ mlxplat_i2c, sizeof(*mlxplat_i2c));
if (IS_ERR(priv->pdev_i2c)) {
err = PTR_ERR(priv->pdev_i2c);
goto fail_alloc;
@@ -2042,21 +2143,8 @@ static int __init mlxplat_init(void)
}
}
- mlxplat_mlxcpld_regmap_ctx.base = devm_ioport_map(&mlxplat_dev->dev,
- mlxplat_lpc_resources[1].start, 1);
- if (!mlxplat_mlxcpld_regmap_ctx.base) {
- err = -ENOMEM;
- goto fail_platform_mux_register;
- }
-
- mlxplat_hotplug->regmap = devm_regmap_init(&mlxplat_dev->dev, NULL,
- &mlxplat_mlxcpld_regmap_ctx,
- &mlxplat_mlxcpld_regmap_config);
- if (IS_ERR(mlxplat_hotplug->regmap)) {
- err = PTR_ERR(mlxplat_hotplug->regmap);
- goto fail_platform_mux_register;
- }
-
+ /* Add hotplug driver */
+ mlxplat_hotplug->regmap = priv->regmap;
priv->pdev_hotplug = platform_device_register_resndata(
&mlxplat_dev->dev, "mlxreg-hotplug",
PLATFORM_DEVID_NONE,
@@ -2069,16 +2157,16 @@ static int __init mlxplat_init(void)
}
/* Set default registers. */
- for (j = 0; j < mlxplat_mlxcpld_regmap_config.num_reg_defaults; j++) {
- err = regmap_write(mlxplat_hotplug->regmap,
- mlxplat_mlxcpld_regmap_default[j].reg,
- mlxplat_mlxcpld_regmap_default[j].def);
+ for (j = 0; j < mlxplat_regmap_config->num_reg_defaults; j++) {
+ err = regmap_write(priv->regmap,
+ mlxplat_regmap_config->reg_defaults[j].reg,
+ mlxplat_regmap_config->reg_defaults[j].def);
if (err)
goto fail_platform_mux_register;
}
/* Add LED driver. */
- mlxplat_led->regmap = mlxplat_hotplug->regmap;
+ mlxplat_led->regmap = priv->regmap;
priv->pdev_led = platform_device_register_resndata(
&mlxplat_dev->dev, "leds-mlxreg",
PLATFORM_DEVID_NONE, NULL, 0,
@@ -2090,7 +2178,7 @@ static int __init mlxplat_init(void)
/* Add registers io access driver. */
if (mlxplat_regs_io) {
- mlxplat_regs_io->regmap = mlxplat_hotplug->regmap;
+ mlxplat_regs_io->regmap = priv->regmap;
priv->pdev_io_regs = platform_device_register_resndata(
&mlxplat_dev->dev, "mlxreg-io",
PLATFORM_DEVID_NONE, NULL, 0,
@@ -2104,7 +2192,7 @@ static int __init mlxplat_init(void)
/* Add FAN driver. */
if (mlxplat_fan) {
- mlxplat_fan->regmap = mlxplat_hotplug->regmap;
+ mlxplat_fan->regmap = priv->regmap;
priv->pdev_fan = platform_device_register_resndata(
&mlxplat_dev->dev, "mlxreg-fan",
PLATFORM_DEVID_NONE, NULL, 0,
@@ -2119,7 +2207,7 @@ static int __init mlxplat_init(void)
/* Add WD drivers. */
for (j = 0; j < MLXPLAT_CPLD_WD_MAX_DEVS; j++) {
if (mlxplat_wd_data[j]) {
- mlxplat_wd_data[j]->regmap = mlxplat_hotplug->regmap;
+ mlxplat_wd_data[j]->regmap = priv->regmap;
priv->pdev_wd[j] = platform_device_register_resndata(
&mlxplat_dev->dev, "mlx-wdt",
j, NULL, 0,
@@ -2133,8 +2221,8 @@ static int __init mlxplat_init(void)
}
/* Sync registers with hardware. */
- regcache_mark_dirty(mlxplat_hotplug->regmap);
- err = regcache_sync(mlxplat_hotplug->regmap);
+ regcache_mark_dirty(priv->regmap);
+ err = regcache_sync(priv->regmap);
if (err)
goto fail_platform_wd_register;
diff --git a/drivers/platform/x86/pcengines-apuv2.c b/drivers/platform/x86/pcengines-apuv2.c
index c1ca931e1fab..b0d3110ae378 100644
--- a/drivers/platform/x86/pcengines-apuv2.c
+++ b/drivers/platform/x86/pcengines-apuv2.c
@@ -77,7 +77,7 @@ static const struct gpio_led_platform_data apu2_leds_pdata = {
.leds = apu2_leds,
};
-struct gpiod_lookup_table gpios_led_table = {
+static struct gpiod_lookup_table gpios_led_table = {
.dev_id = "leds-gpio",
.table = {
GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED1,
@@ -110,7 +110,7 @@ static const struct gpio_keys_platform_data apu2_keys_pdata = {
.name = "apu2-keys",
};
-struct gpiod_lookup_table gpios_key_table = {
+static struct gpiod_lookup_table gpios_key_table = {
.dev_id = "gpio-keys-polled",
.table = {
GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_MODESW,
diff --git a/drivers/platform/x86/pmc_atom.c b/drivers/platform/x86/pmc_atom.c
index be802fd2182d..aa53648a2214 100644
--- a/drivers/platform/x86/pmc_atom.c
+++ b/drivers/platform/x86/pmc_atom.c
@@ -341,45 +341,24 @@ static int pmc_sleep_tmr_show(struct seq_file *s, void *unused)
DEFINE_SHOW_ATTRIBUTE(pmc_sleep_tmr);
-static void pmc_dbgfs_unregister(struct pmc_dev *pmc)
+static void pmc_dbgfs_register(struct pmc_dev *pmc)
{
- debugfs_remove_recursive(pmc->dbgfs_dir);
-}
-
-static int pmc_dbgfs_register(struct pmc_dev *pmc)
-{
- struct dentry *dir, *f;
+ struct dentry *dir;
dir = debugfs_create_dir("pmc_atom", NULL);
- if (!dir)
- return -ENOMEM;
pmc->dbgfs_dir = dir;
- f = debugfs_create_file("dev_state", S_IFREG | S_IRUGO,
- dir, pmc, &pmc_dev_state_fops);
- if (!f)
- goto err;
-
- f = debugfs_create_file("pss_state", S_IFREG | S_IRUGO,
- dir, pmc, &pmc_pss_state_fops);
- if (!f)
- goto err;
-
- f = debugfs_create_file("sleep_state", S_IFREG | S_IRUGO,
- dir, pmc, &pmc_sleep_tmr_fops);
- if (!f)
- goto err;
-
- return 0;
-err:
- pmc_dbgfs_unregister(pmc);
- return -ENODEV;
+ debugfs_create_file("dev_state", S_IFREG | S_IRUGO, dir, pmc,
+ &pmc_dev_state_fops);
+ debugfs_create_file("pss_state", S_IFREG | S_IRUGO, dir, pmc,
+ &pmc_pss_state_fops);
+ debugfs_create_file("sleep_state", S_IFREG | S_IRUGO, dir, pmc,
+ &pmc_sleep_tmr_fops);
}
#else
-static int pmc_dbgfs_register(struct pmc_dev *pmc)
+static void pmc_dbgfs_register(struct pmc_dev *pmc)
{
- return 0;
}
#endif /* CONFIG_DEBUG_FS */
@@ -414,6 +393,14 @@ static const struct dmi_system_id critclk_systems[] = {
},
{
/* pmc_plt_clk* - are used for ethernet controllers */
+ .ident = "Beckhoff CB4063",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Beckhoff Automation"),
+ DMI_MATCH(DMI_BOARD_NAME, "CB4063"),
+ },
+ },
+ {
+ /* pmc_plt_clk* - are used for ethernet controllers */
.ident = "Beckhoff CB6263",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Beckhoff Automation"),
@@ -491,9 +478,7 @@ static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent)
/* PMC hardware registers setup */
pmc_hw_reg_setup(pmc);
- ret = pmc_dbgfs_register(pmc);
- if (ret)
- dev_warn(&pdev->dev, "debugfs register failed\n");
+ pmc_dbgfs_register(pmc);
/* Register platform clocks - PMC_PLT_CLK [0..5] */
ret = pmc_setup_clks(pdev, pmc->regmap, data);
diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c
index 123e52c73c86..9b6a93ff41ff 100644
--- a/drivers/platform/x86/samsung-laptop.c
+++ b/drivers/platform/x86/samsung-laptop.c
@@ -1276,15 +1276,12 @@ static void samsung_debugfs_exit(struct samsung_laptop *samsung)
debugfs_remove_recursive(samsung->debug.root);
}
-static int samsung_debugfs_init(struct samsung_laptop *samsung)
+static void samsung_debugfs_init(struct samsung_laptop *samsung)
{
- struct dentry *dent;
+ struct dentry *root;
- samsung->debug.root = debugfs_create_dir("samsung-laptop", NULL);
- if (!samsung->debug.root) {
- pr_err("failed to create debugfs directory");
- goto error_debugfs;
- }
+ root = debugfs_create_dir("samsung-laptop", NULL);
+ samsung->debug.root = root;
samsung->debug.f0000_wrapper.data = samsung->f0000_segment;
samsung->debug.f0000_wrapper.size = 0xffff;
@@ -1295,60 +1292,24 @@ static int samsung_debugfs_init(struct samsung_laptop *samsung)
samsung->debug.sdiag_wrapper.data = samsung->sdiag;
samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag);
- dent = debugfs_create_u16("command", S_IRUGO | S_IWUSR,
- samsung->debug.root, &samsung->debug.command);
- if (!dent)
- goto error_debugfs;
-
- dent = debugfs_create_u32("d0", S_IRUGO | S_IWUSR, samsung->debug.root,
- &samsung->debug.data.d0);
- if (!dent)
- goto error_debugfs;
-
- dent = debugfs_create_u32("d1", S_IRUGO | S_IWUSR, samsung->debug.root,
- &samsung->debug.data.d1);
- if (!dent)
- goto error_debugfs;
-
- dent = debugfs_create_u16("d2", S_IRUGO | S_IWUSR, samsung->debug.root,
- &samsung->debug.data.d2);
- if (!dent)
- goto error_debugfs;
-
- dent = debugfs_create_u8("d3", S_IRUGO | S_IWUSR, samsung->debug.root,
- &samsung->debug.data.d3);
- if (!dent)
- goto error_debugfs;
-
- dent = debugfs_create_blob("data", S_IRUGO | S_IWUSR,
- samsung->debug.root,
- &samsung->debug.data_wrapper);
- if (!dent)
- goto error_debugfs;
-
- dent = debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR,
- samsung->debug.root,
- &samsung->debug.f0000_wrapper);
- if (!dent)
- goto error_debugfs;
-
- dent = debugfs_create_file("call", S_IFREG | S_IRUGO,
- samsung->debug.root, samsung,
- &samsung_laptop_call_fops);
- if (!dent)
- goto error_debugfs;
-
- dent = debugfs_create_blob("sdiag", S_IRUGO | S_IWUSR,
- samsung->debug.root,
- &samsung->debug.sdiag_wrapper);
- if (!dent)
- goto error_debugfs;
-
- return 0;
-
-error_debugfs:
- samsung_debugfs_exit(samsung);
- return -ENOMEM;
+ debugfs_create_u16("command", S_IRUGO | S_IWUSR, root,
+ &samsung->debug.command);
+ debugfs_create_u32("d0", S_IRUGO | S_IWUSR, root,
+ &samsung->debug.data.d0);
+ debugfs_create_u32("d1", S_IRUGO | S_IWUSR, root,
+ &samsung->debug.data.d1);
+ debugfs_create_u16("d2", S_IRUGO | S_IWUSR, root,
+ &samsung->debug.data.d2);
+ debugfs_create_u8("d3", S_IRUGO | S_IWUSR, root,
+ &samsung->debug.data.d3);
+ debugfs_create_blob("data", S_IRUGO | S_IWUSR, root,
+ &samsung->debug.data_wrapper);
+ debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR, root,
+ &samsung->debug.f0000_wrapper);
+ debugfs_create_file("call", S_IFREG | S_IRUGO, root, samsung,
+ &samsung_laptop_call_fops);
+ debugfs_create_blob("sdiag", S_IRUGO | S_IWUSR, root,
+ &samsung->debug.sdiag_wrapper);
}
static void samsung_sabi_exit(struct samsung_laptop *samsung)
@@ -1741,9 +1702,7 @@ static int __init samsung_init(void)
if (ret)
goto error_lid_handling;
- ret = samsung_debugfs_init(samsung);
- if (ret)
- goto error_debugfs;
+ samsung_debugfs_init(samsung);
samsung->pm_nb.notifier_call = samsung_pm_notification;
register_pm_notifier(&samsung->pm_nb);
@@ -1751,8 +1710,6 @@ static int __init samsung_init(void)
samsung_platform_device = samsung->platform_device;
return ret;
-error_debugfs:
- samsung_lid_handling_exit(samsung);
error_lid_handling:
samsung_leds_exit(samsung);
error_leds:
diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c
index b662cb2d7cd5..4370e4add83a 100644
--- a/drivers/platform/x86/touchscreen_dmi.c
+++ b/drivers/platform/x86/touchscreen_dmi.c
@@ -87,6 +87,22 @@ static const struct ts_dmi_data chuwi_hi10_air_data = {
.properties = chuwi_hi10_air_props,
};
+static const struct property_entry chuwi_hi10_plus_props[] = {
+ PROPERTY_ENTRY_U32("touchscreen-min-x", 0),
+ PROPERTY_ENTRY_U32("touchscreen-min-y", 5),
+ PROPERTY_ENTRY_U32("touchscreen-size-x", 1914),
+ PROPERTY_ENTRY_U32("touchscreen-size-y", 1283),
+ PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi10plus.fw"),
+ PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+ PROPERTY_ENTRY_BOOL("silead,home-button"),
+ { }
+};
+
+static const struct ts_dmi_data chuwi_hi10_plus_data = {
+ .acpi_name = "MSSL0017:00",
+ .properties = chuwi_hi10_plus_props,
+};
+
static const struct property_entry chuwi_vi8_props[] = {
PROPERTY_ENTRY_U32("touchscreen-min-x", 4),
PROPERTY_ENTRY_U32("touchscreen-min-y", 6),
@@ -597,11 +613,21 @@ static const struct dmi_system_id touchscreen_dmi_table[] = {
/* Chuwi Hi10 Air */
.driver_data = (void *)&chuwi_hi10_air_data,
.matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"),
+ DMI_MATCH(DMI_SYS_VENDOR, "CHUWI INNOVATION AND TECHNOLOGY(SHENZHEN)CO.LTD"),
+ DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
DMI_MATCH(DMI_PRODUCT_SKU, "P1W6_C109D_B"),
},
},
{
+ /* Chuwi Hi10 Plus (CWI527) */
+ .driver_data = (void *)&chuwi_hi10_plus_data,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Hi10 plus tablet"),
+ DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
+ },
+ },
+ {
/* Chuwi Vi8 (CWI506) */
.driver_data = (void *)&chuwi_vi8_data,
.matches = {
diff --git a/drivers/platform/x86/wmi-bmof.c b/drivers/platform/x86/wmi-bmof.c
index c3f63411f9ab..66b434d6307f 100644
--- a/drivers/platform/x86/wmi-bmof.c
+++ b/drivers/platform/x86/wmi-bmof.c
@@ -46,7 +46,7 @@ read_bmof(struct file *filp, struct kobject *kobj,
return count;
}
-static int wmi_bmof_probe(struct wmi_device *wdev)
+static int wmi_bmof_probe(struct wmi_device *wdev, const void *context)
{
struct bmof_priv *priv;
int ret;
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index 22f4b92c5da4..784cea8572c2 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -129,6 +129,28 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)
return false;
}
+static const void *find_guid_context(struct wmi_block *wblock,
+ struct wmi_driver *wdriver)
+{
+ const struct wmi_device_id *id;
+ uuid_le guid_input;
+
+ if (wblock == NULL || wdriver == NULL)
+ return NULL;
+ if (wdriver->id_table == NULL)
+ return NULL;
+
+ id = wdriver->id_table;
+ while (*id->guid_string) {
+ if (uuid_le_to_bin(id->guid_string, &guid_input))
+ continue;
+ if (!memcmp(wblock->gblock.guid, &guid_input, 16))
+ return id->context;
+ id++;
+ }
+ return NULL;
+}
+
static int get_subobj_info(acpi_handle handle, const char *pathname,
struct acpi_device_info **info)
{
@@ -618,6 +640,25 @@ bool wmi_has_guid(const char *guid_string)
}
EXPORT_SYMBOL_GPL(wmi_has_guid);
+/**
+ * wmi_get_acpi_device_uid() - Get _UID name of ACPI device that defines GUID
+ * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
+ *
+ * Find the _UID of ACPI device associated with this WMI GUID.
+ *
+ * Return: The ACPI _UID field value or NULL if the WMI GUID was not found
+ */
+char *wmi_get_acpi_device_uid(const char *guid_string)
+{
+ struct wmi_block *wblock = NULL;
+
+ if (!find_guid(guid_string, &wblock))
+ return NULL;
+
+ return acpi_device_uid(wblock->acpi_device);
+}
+EXPORT_SYMBOL_GPL(wmi_get_acpi_device_uid);
+
static struct wmi_block *dev_to_wblock(struct device *dev)
{
return container_of(dev, struct wmi_block, dev.dev);
@@ -887,7 +928,8 @@ static int wmi_dev_probe(struct device *dev)
dev_warn(dev, "failed to enable device -- probing anyway\n");
if (wdriver->probe) {
- ret = wdriver->probe(dev_to_wdev(dev));
+ ret = wdriver->probe(dev_to_wdev(dev),
+ find_guid_context(wblock, wdriver));
if (ret != 0)
goto probe_failure;
}
diff --git a/drivers/platform/x86/xiaomi-wmi.c b/drivers/platform/x86/xiaomi-wmi.c
new file mode 100644
index 000000000000..601cbb282f54
--- /dev/null
+++ b/drivers/platform/x86/xiaomi-wmi.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0
+/* WMI driver for Xiaomi Laptops */
+
+#include <linux/acpi.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/wmi.h>
+
+#include <uapi/linux/input-event-codes.h>
+
+#define XIAOMI_KEY_FN_ESC_0 "A2095CCE-0491-44E7-BA27-F8ED8F88AA86"
+#define XIAOMI_KEY_FN_ESC_1 "7BBE8E39-B486-473D-BA13-66F75C5805CD"
+#define XIAOMI_KEY_FN_FN "409B028D-F06B-4C7C-8BBB-EE133A6BD87E"
+#define XIAOMI_KEY_CAPSLOCK "83FE7607-053A-4644-822A-21532C621FC7"
+#define XIAOMI_KEY_FN_F7 "76E9027C-95D0-4180-8692-DA6747DD1C2D"
+
+#define XIAOMI_DEVICE(guid, key) \
+ .guid_string = (guid), \
+ .context = &(const unsigned int){key}
+
+struct xiaomi_wmi {
+ struct input_dev *input_dev;
+ unsigned int key_code;
+};
+
+int xiaomi_wmi_probe(struct wmi_device *wdev, const void *context)
+{
+ struct xiaomi_wmi *data;
+
+ if (wdev == NULL || context == NULL)
+ return -EINVAL;
+
+ data = devm_kzalloc(&wdev->dev, sizeof(struct xiaomi_wmi), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+ dev_set_drvdata(&wdev->dev, data);
+
+ data->input_dev = devm_input_allocate_device(&wdev->dev);
+ if (data->input_dev == NULL)
+ return -ENOMEM;
+ data->input_dev->name = "Xiaomi WMI keys";
+ data->input_dev->phys = "wmi/input0";
+
+ data->key_code = *((const unsigned int *)context);
+ set_bit(EV_KEY, data->input_dev->evbit);
+ set_bit(data->key_code, data->input_dev->keybit);
+
+ return input_register_device(data->input_dev);
+}
+
+void xiaomi_wmi_notify(struct wmi_device *wdev, union acpi_object *dummy)
+{
+ struct xiaomi_wmi *data;
+
+ if (wdev == NULL)
+ return;
+
+ data = dev_get_drvdata(&wdev->dev);
+ if (data == NULL)
+ return;
+
+ input_report_key(data->input_dev, data->key_code, 1);
+ input_sync(data->input_dev);
+ input_report_key(data->input_dev, data->key_code, 0);
+ input_sync(data->input_dev);
+}
+
+static const struct wmi_device_id xiaomi_wmi_id_table[] = {
+ // { XIAOMI_DEVICE(XIAOMI_KEY_FN_ESC_0, KEY_FN_ESC) },
+ // { XIAOMI_DEVICE(XIAOMI_KEY_FN_ESC_1, KEY_FN_ESC) },
+ { XIAOMI_DEVICE(XIAOMI_KEY_FN_FN, KEY_PROG1) },
+ // { XIAOMI_DEVICE(XIAOMI_KEY_CAPSLOCK, KEY_CAPSLOCK) },
+ { XIAOMI_DEVICE(XIAOMI_KEY_FN_F7, KEY_CUT) },
+
+ /* Terminating entry */
+ { }
+};
+
+static struct wmi_driver xiaomi_wmi_driver = {
+ .driver = {
+ .name = "xiaomi-wmi",
+ },
+ .id_table = xiaomi_wmi_id_table,
+ .probe = xiaomi_wmi_probe,
+ .notify = xiaomi_wmi_notify,
+};
+module_wmi_driver(xiaomi_wmi_driver);
+
+MODULE_DEVICE_TABLE(wmi, xiaomi_wmi_id_table);
+MODULE_AUTHOR("Mattias Jacobsson");
+MODULE_DESCRIPTION("Xiaomi WMI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index dd7da41f230c..c00c33ce06ce 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -152,7 +152,7 @@ config BATTERY_PMU
config BATTERY_OLPC
tristate "One Laptop Per Child battery"
- depends on X86_32 && OLPC
+ depends on OLPC_EC
help
Say Y to enable support for the battery on the OLPC laptop.
diff --git a/drivers/power/supply/olpc_battery.c b/drivers/power/supply/olpc_battery.c
index 9f9430ac8887..ad0e9e0edb3f 100644
--- a/drivers/power/supply/olpc_battery.c
+++ b/drivers/power/supply/olpc_battery.c
@@ -17,7 +17,6 @@
#include <linux/jiffies.h>
#include <linux/sched.h>
#include <linux/olpc-ec.h>
-#include <asm/olpc.h>
#define EC_BAT_VOLTAGE 0x10 /* uint16_t, *9.76/32, mV */
diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
index 91d65f337d87..f91db24bbf3b 100644
--- a/fs/ecryptfs/crypto.c
+++ b/fs/ecryptfs/crypto.c
@@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <asm/unaligned.h>
#include <linux/kernel.h>
+#include <linux/xattr.h>
#include "ecryptfs_kernel.h"
#define DECRYPT 0
@@ -860,13 +861,10 @@ static struct ecryptfs_flag_map_elem ecryptfs_flag_map[] = {
* @crypt_stat: The cryptographic context
* @page_virt: Source data to be parsed
* @bytes_read: Updated with the number of bytes read
- *
- * Returns zero on success; non-zero if the flag set is invalid
*/
-static int ecryptfs_process_flags(struct ecryptfs_crypt_stat *crypt_stat,
+static void ecryptfs_process_flags(struct ecryptfs_crypt_stat *crypt_stat,
char *page_virt, int *bytes_read)
{
- int rc = 0;
int i;
u32 flags;
@@ -879,7 +877,6 @@ static int ecryptfs_process_flags(struct ecryptfs_crypt_stat *crypt_stat,
/* Version is in top 8 bits of the 32-bit flag vector */
crypt_stat->file_version = ((flags >> 24) & 0xFF);
(*bytes_read) = 4;
- return rc;
}
/**
@@ -1004,8 +1001,10 @@ int ecryptfs_read_and_validate_header_region(struct inode *inode)
rc = ecryptfs_read_lower(file_size, 0, ECRYPTFS_SIZE_AND_MARKER_BYTES,
inode);
- if (rc < ECRYPTFS_SIZE_AND_MARKER_BYTES)
- return rc >= 0 ? -EINVAL : rc;
+ if (rc < 0)
+ return rc;
+ else if (rc < ECRYPTFS_SIZE_AND_MARKER_BYTES)
+ return -EINVAL;
rc = ecryptfs_validate_marker(marker);
if (!rc)
ecryptfs_i_size_init(file_size, inode);
@@ -1115,9 +1114,21 @@ ecryptfs_write_metadata_to_xattr(struct dentry *ecryptfs_dentry,
char *page_virt, size_t size)
{
int rc;
+ struct dentry *lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry);
+ struct inode *lower_inode = d_inode(lower_dentry);
- rc = ecryptfs_setxattr(ecryptfs_dentry, ecryptfs_inode,
- ECRYPTFS_XATTR_NAME, page_virt, size, 0);
+ if (!(lower_inode->i_opflags & IOP_XATTR)) {
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
+ inode_lock(lower_inode);
+ rc = __vfs_setxattr(lower_dentry, lower_inode, ECRYPTFS_XATTR_NAME,
+ page_virt, size, 0);
+ if (!rc && ecryptfs_inode)
+ fsstack_copy_attr_all(ecryptfs_inode, lower_inode);
+ inode_unlock(lower_inode);
+out:
return rc;
}
@@ -1291,12 +1302,7 @@ static int ecryptfs_read_headers_virt(char *page_virt,
if (!(crypt_stat->flags & ECRYPTFS_I_SIZE_INITIALIZED))
ecryptfs_i_size_init(page_virt, d_inode(ecryptfs_dentry));
offset += MAGIC_ECRYPTFS_MARKER_SIZE_BYTES;
- rc = ecryptfs_process_flags(crypt_stat, (page_virt + offset),
- &bytes_read);
- if (rc) {
- ecryptfs_printk(KERN_WARNING, "Error processing flags\n");
- goto out;
- }
+ ecryptfs_process_flags(crypt_stat, (page_virt + offset), &bytes_read);
if (crypt_stat->file_version > ECRYPTFS_SUPPORTED_FILE_VERSION) {
ecryptfs_printk(KERN_WARNING, "File version is [%d]; only "
"file version [%d] is supported by this "
@@ -1367,8 +1373,10 @@ int ecryptfs_read_and_validate_xattr_region(struct dentry *dentry,
ecryptfs_inode_to_lower(inode),
ECRYPTFS_XATTR_NAME, file_size,
ECRYPTFS_SIZE_AND_MARKER_BYTES);
- if (rc < ECRYPTFS_SIZE_AND_MARKER_BYTES)
- return rc >= 0 ? -EINVAL : rc;
+ if (rc < 0)
+ return rc;
+ else if (rc < ECRYPTFS_SIZE_AND_MARKER_BYTES)
+ return -EINVAL;
rc = ecryptfs_validate_marker(marker);
if (!rc)
ecryptfs_i_size_init(file_size, inode);
diff --git a/fs/ecryptfs/debug.c b/fs/ecryptfs/debug.c
index d131d070826f..1f65e99f9a41 100644
--- a/fs/ecryptfs/debug.c
+++ b/fs/ecryptfs/debug.c
@@ -83,25 +83,9 @@ void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok)
*/
void ecryptfs_dump_hex(char *data, int bytes)
{
- int i = 0;
- int add_newline = 1;
-
if (ecryptfs_verbosity < 1)
return;
- if (bytes != 0) {
- printk(KERN_DEBUG "0x%.2x.", (unsigned char)data[i]);
- i++;
- }
- while (i < bytes) {
- printk("0x%.2x.", (unsigned char)data[i]);
- i++;
- if (i % 16 == 0) {
- printk("\n");
- add_newline = 0;
- } else
- add_newline = 1;
- }
- if (add_newline)
- printk("\n");
-}
+ print_hex_dump(KERN_DEBUG, "ecryptfs: ", DUMP_PREFIX_OFFSET, 16, 1,
+ data, bytes, false);
+}
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 1e994d780f37..18426f4855f1 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -1121,7 +1121,7 @@ static int ecryptfs_xattr_set(const struct xattr_handler *handler,
}
}
-const struct xattr_handler ecryptfs_xattr_handler = {
+static const struct xattr_handler ecryptfs_xattr_handler = {
.prefix = "", /* match anything */
.get = ecryptfs_xattr_get,
.set = ecryptfs_xattr_set,
diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c
index 9536e592e25a..216fbe6a4837 100644
--- a/fs/ecryptfs/keystore.c
+++ b/fs/ecryptfs/keystore.c
@@ -1048,8 +1048,9 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
"rc = [%d]\n", __func__, rc);
goto out_free_unlock;
}
- while (s->decrypted_filename[s->i] != '\0'
- && s->i < s->block_aligned_filename_size)
+
+ while (s->i < s->block_aligned_filename_size &&
+ s->decrypted_filename[s->i] != '\0')
s->i++;
if (s->i == s->block_aligned_filename_size) {
printk(KERN_WARNING "%s: Invalid tag 70 packet; could not "
@@ -1611,9 +1612,9 @@ int ecryptfs_keyring_auth_tok_for_sig(struct key **auth_tok_key,
int rc = 0;
(*auth_tok_key) = request_key(&key_type_user, sig, NULL);
- if (!(*auth_tok_key) || IS_ERR(*auth_tok_key)) {
+ if (IS_ERR(*auth_tok_key)) {
(*auth_tok_key) = ecryptfs_get_encrypted_key(sig);
- if (!(*auth_tok_key) || IS_ERR(*auth_tok_key)) {
+ if (IS_ERR(*auth_tok_key)) {
printk(KERN_ERR "Could not find key with description: [%s]\n",
sig);
rc = process_request_key_err(PTR_ERR(*auth_tok_key));
diff --git a/fs/io_uring.c b/fs/io_uring.c
index 3fd884b4e0be..d682049c07b2 100644
--- a/fs/io_uring.c
+++ b/fs/io_uring.c
@@ -399,7 +399,8 @@ static struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p)
if (!ctx)
return NULL;
- if (percpu_ref_init(&ctx->refs, io_ring_ctx_ref_free, 0, GFP_KERNEL)) {
+ if (percpu_ref_init(&ctx->refs, io_ring_ctx_ref_free,
+ PERCPU_REF_ALLOW_REINIT, GFP_KERNEL)) {
kfree(ctx);
return NULL;
}
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index dedca3da428a..818cedbed95f 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -1322,7 +1322,7 @@ static pagemap_entry_t pte_to_pagemap_entry(struct pagemapread *pm,
if (pm->show_pfn)
frame = pte_pfn(pte);
flags |= PM_PRESENT;
- page = _vm_normal_page(vma, addr, pte, true);
+ page = vm_normal_page(vma, addr, pte);
if (pte_soft_dirty(pte))
flags |= PM_SOFT_DIRTY;
} else if (is_swap_pte(pte)) {
diff --git a/fs/ubifs/Kconfig b/fs/ubifs/Kconfig
index 06c35c64162b..69932bcfa920 100644
--- a/fs/ubifs/Kconfig
+++ b/fs/ubifs/Kconfig
@@ -6,8 +6,10 @@ config UBIFS_FS
select CRYPTO if UBIFS_FS_ADVANCED_COMPR
select CRYPTO if UBIFS_FS_LZO
select CRYPTO if UBIFS_FS_ZLIB
+ select CRYPTO if UBIFS_FS_ZSTD
select CRYPTO_LZO if UBIFS_FS_LZO
select CRYPTO_DEFLATE if UBIFS_FS_ZLIB
+ select CRYPTO_ZSTD if UBIFS_FS_ZSTD
select CRYPTO_HASH_INFO
select UBIFS_FS_XATTR if FS_ENCRYPTION
depends on MTD_UBI
@@ -38,6 +40,14 @@ config UBIFS_FS_ZLIB
help
Zlib compresses better than LZO but it is slower. Say 'Y' if unsure.
+config UBIFS_FS_ZSTD
+ bool "ZSTD compression support" if UBIFS_FS_ADVANCED_COMPR
+ depends on UBIFS_FS
+ default y
+ help
+ ZSTD compresses is a big win in speed over Zlib and
+ in compression ratio over LZO. Say 'Y' if unsure.
+
config UBIFS_ATIME_SUPPORT
bool "Access time support"
default n
@@ -77,8 +87,9 @@ config UBIFS_FS_SECURITY
config UBIFS_FS_AUTHENTICATION
bool "UBIFS authentication support"
- depends on KEYS
+ select KEYS
select CRYPTO_HMAC
+ select SYSTEM_DATA_VERIFICATION
help
Enable authentication support for UBIFS. This feature offers protection
against offline changes for both data and metadata of the filesystem.
diff --git a/fs/ubifs/auth.c b/fs/ubifs/auth.c
index 60f43b93d06e..d9af2de9084a 100644
--- a/fs/ubifs/auth.c
+++ b/fs/ubifs/auth.c
@@ -10,10 +10,12 @@
*/
#include <linux/crypto.h>
+#include <linux/verification.h>
#include <crypto/hash.h>
#include <crypto/sha.h>
#include <crypto/algapi.h>
#include <keys/user-type.h>
+#include <keys/asymmetric-type.h>
#include "ubifs.h"
@@ -199,6 +201,77 @@ int __ubifs_node_check_hash(const struct ubifs_info *c, const void *node,
}
/**
+ * ubifs_sb_verify_signature - verify the signature of a superblock
+ * @c: UBIFS file-system description object
+ * @sup: The superblock node
+ *
+ * To support offline signed images the superblock can be signed with a
+ * PKCS#7 signature. The signature is placed directly behind the superblock
+ * node in an ubifs_sig_node.
+ *
+ * Returns 0 when the signature can be successfully verified or a negative
+ * error code if not.
+ */
+int ubifs_sb_verify_signature(struct ubifs_info *c,
+ const struct ubifs_sb_node *sup)
+{
+ int err;
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+ const struct ubifs_sig_node *signode;
+
+ sleb = ubifs_scan(c, UBIFS_SB_LNUM, UBIFS_SB_NODE_SZ, c->sbuf, 0);
+ if (IS_ERR(sleb)) {
+ err = PTR_ERR(sleb);
+ return err;
+ }
+
+ if (sleb->nodes_cnt == 0) {
+ ubifs_err(c, "Unable to find signature node");
+ err = -EINVAL;
+ goto out_destroy;
+ }
+
+ snod = list_first_entry(&sleb->nodes, struct ubifs_scan_node, list);
+
+ if (snod->type != UBIFS_SIG_NODE) {
+ ubifs_err(c, "Signature node is of wrong type");
+ err = -EINVAL;
+ goto out_destroy;
+ }
+
+ signode = snod->node;
+
+ if (le32_to_cpu(signode->len) > snod->len + sizeof(struct ubifs_sig_node)) {
+ ubifs_err(c, "invalid signature len %d", le32_to_cpu(signode->len));
+ err = -EINVAL;
+ goto out_destroy;
+ }
+
+ if (le32_to_cpu(signode->type) != UBIFS_SIGNATURE_TYPE_PKCS7) {
+ ubifs_err(c, "Signature type %d is not supported\n",
+ le32_to_cpu(signode->type));
+ err = -EINVAL;
+ goto out_destroy;
+ }
+
+ err = verify_pkcs7_signature(sup, sizeof(struct ubifs_sb_node),
+ signode->sig, le32_to_cpu(signode->len),
+ NULL, VERIFYING_UNSPECIFIED_SIGNATURE,
+ NULL, NULL);
+
+ if (err)
+ ubifs_err(c, "Failed to verify signature");
+ else
+ ubifs_msg(c, "Successfully verified super block signature");
+
+out_destroy:
+ ubifs_scan_destroy(sleb);
+
+ return err;
+}
+
+/**
* ubifs_init_authentication - initialize UBIFS authentication support
* @c: UBIFS file-system description object
*
@@ -478,3 +551,16 @@ int ubifs_hmac_wkm(struct ubifs_info *c, u8 *hmac)
return err;
return 0;
}
+
+/*
+ * ubifs_hmac_zero - test if a HMAC is zero
+ * @c: UBIFS file-system description object
+ * @hmac: the HMAC to test
+ *
+ * This function tests if a HMAC is zero and returns true if it is
+ * and false otherwise.
+ */
+bool ubifs_hmac_zero(struct ubifs_info *c, const u8 *hmac)
+{
+ return !memchr_inv(hmac, 0, c->hmac_desc_len);
+}
diff --git a/fs/ubifs/compress.c b/fs/ubifs/compress.c
index 99c53ad11e93..3a92e6af69b2 100644
--- a/fs/ubifs/compress.c
+++ b/fs/ubifs/compress.c
@@ -59,6 +59,24 @@ static struct ubifs_compressor zlib_compr = {
};
#endif
+#ifdef CONFIG_UBIFS_FS_ZSTD
+static DEFINE_MUTEX(zstd_enc_mutex);
+static DEFINE_MUTEX(zstd_dec_mutex);
+
+static struct ubifs_compressor zstd_compr = {
+ .compr_type = UBIFS_COMPR_ZSTD,
+ .comp_mutex = &zstd_enc_mutex,
+ .decomp_mutex = &zstd_dec_mutex,
+ .name = "zstd",
+ .capi_name = "zstd",
+};
+#else
+static struct ubifs_compressor zstd_compr = {
+ .compr_type = UBIFS_COMPR_ZSTD,
+ .name = "zstd",
+};
+#endif
+
/* All UBIFS compressors */
struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
@@ -216,13 +234,19 @@ int __init ubifs_compressors_init(void)
if (err)
return err;
- err = compr_init(&zlib_compr);
+ err = compr_init(&zstd_compr);
if (err)
goto out_lzo;
+ err = compr_init(&zlib_compr);
+ if (err)
+ goto out_zstd;
+
ubifs_compressors[UBIFS_COMPR_NONE] = &none_compr;
return 0;
+out_zstd:
+ compr_exit(&zstd_compr);
out_lzo:
compr_exit(&lzo_compr);
return err;
@@ -235,4 +259,5 @@ void ubifs_compressors_exit(void)
{
compr_exit(&lzo_compr);
compr_exit(&zlib_compr);
+ compr_exit(&zstd_compr);
}
diff --git a/fs/ubifs/log.c b/fs/ubifs/log.c
index cd85d7d4c515..b6ac9c4281ef 100644
--- a/fs/ubifs/log.c
+++ b/fs/ubifs/log.c
@@ -438,10 +438,7 @@ int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum)
*ltail_lnum = c->lhead_lnum;
c->lhead_offs += len;
- if (c->lhead_offs == c->leb_size) {
- c->lhead_lnum = ubifs_next_log_lnum(c, c->lhead_lnum);
- c->lhead_offs = 0;
- }
+ ubifs_assert(c, c->lhead_offs < c->leb_size);
remove_buds(c);
diff --git a/fs/ubifs/master.c b/fs/ubifs/master.c
index b42a768709c0..52a85c01397e 100644
--- a/fs/ubifs/master.c
+++ b/fs/ubifs/master.c
@@ -48,6 +48,39 @@ int ubifs_compare_master_node(struct ubifs_info *c, void *m1, void *m2)
return 0;
}
+/* mst_node_check_hash - Check hash of a master node
+ * @c: UBIFS file-system description object
+ * @mst: The master node
+ * @expected: The expected hash of the master node
+ *
+ * This checks the hash of a master node against a given expected hash.
+ * Note that we have two master nodes on a UBIFS image which have different
+ * sequence numbers and consequently different CRCs. To be able to match
+ * both master nodes we exclude the common node header containing the sequence
+ * number and CRC from the hash.
+ *
+ * Returns 0 if the hashes are equal, a negative error code otherwise.
+ */
+static int mst_node_check_hash(const struct ubifs_info *c,
+ const struct ubifs_mst_node *mst,
+ const u8 *expected)
+{
+ 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);
+
+ if (ubifs_check_hash(c, expected, calc))
+ return -EPERM;
+
+ return 0;
+}
+
/**
* scan_for_master - search the valid master node.
* @c: UBIFS file-system description object
@@ -102,14 +135,22 @@ static int scan_for_master(struct ubifs_info *c)
if (!ubifs_authenticated(c))
return 0;
- err = ubifs_node_verify_hmac(c, c->mst_node,
- sizeof(struct ubifs_mst_node),
- offsetof(struct ubifs_mst_node, hmac));
- if (err) {
- ubifs_err(c, "Failed to verify master node HMAC");
- return -EPERM;
+ if (ubifs_hmac_zero(c, c->mst_node->hmac)) {
+ err = mst_node_check_hash(c, c->mst_node,
+ c->sup_node->hash_mst);
+ if (err)
+ ubifs_err(c, "Failed to verify master node hash");
+ } else {
+ err = ubifs_node_verify_hmac(c, c->mst_node,
+ sizeof(struct ubifs_mst_node),
+ offsetof(struct ubifs_mst_node, hmac));
+ if (err)
+ ubifs_err(c, "Failed to verify master node HMAC");
}
+ if (err)
+ return -EPERM;
+
return 0;
out:
diff --git a/fs/ubifs/orphan.c b/fs/ubifs/orphan.c
index cb72688032cd..b52624e28fa1 100644
--- a/fs/ubifs/orphan.c
+++ b/fs/ubifs/orphan.c
@@ -126,25 +126,11 @@ static void __orphan_drop(struct ubifs_info *c, struct ubifs_orphan *o)
kfree(o);
}
-static void orphan_delete(struct ubifs_info *c, ino_t inum)
+static void orphan_delete(struct ubifs_info *c, struct ubifs_orphan *orph)
{
- struct ubifs_orphan *orph, *child_orph, *tmp_o;
-
- spin_lock(&c->orphan_lock);
-
- orph = lookup_orphan(c, inum);
- if (!orph) {
- spin_unlock(&c->orphan_lock);
- ubifs_err(c, "missing orphan ino %lu", (unsigned long)inum);
- dump_stack();
-
- return;
- }
-
if (orph->del) {
spin_unlock(&c->orphan_lock);
- dbg_gen("deleted twice ino %lu",
- (unsigned long)inum);
+ dbg_gen("deleted twice ino %lu", orph->inum);
return;
}
@@ -153,19 +139,11 @@ static void orphan_delete(struct ubifs_info *c, ino_t inum)
orph->dnext = c->orph_dnext;
c->orph_dnext = orph;
spin_unlock(&c->orphan_lock);
- dbg_gen("delete later ino %lu",
- (unsigned long)inum);
+ dbg_gen("delete later ino %lu", orph->inum);
return;
}
- list_for_each_entry_safe(child_orph, tmp_o, &orph->child_list, child_list) {
- list_del(&child_orph->child_list);
- __orphan_drop(c, child_orph);
- }
-
__orphan_drop(c, orph);
-
- spin_unlock(&c->orphan_lock);
}
/**
@@ -223,7 +201,27 @@ int ubifs_add_orphan(struct ubifs_info *c, ino_t inum)
*/
void ubifs_delete_orphan(struct ubifs_info *c, ino_t inum)
{
- orphan_delete(c, inum);
+ struct ubifs_orphan *orph, *child_orph, *tmp_o;
+
+ spin_lock(&c->orphan_lock);
+
+ orph = lookup_orphan(c, inum);
+ if (!orph) {
+ spin_unlock(&c->orphan_lock);
+ ubifs_err(c, "missing orphan ino %lu", (unsigned long)inum);
+ dump_stack();
+
+ return;
+ }
+
+ list_for_each_entry_safe(child_orph, tmp_o, &orph->child_list, child_list) {
+ list_del(&child_orph->child_list);
+ orphan_delete(c, child_orph);
+ }
+
+ orphan_delete(c, orph);
+
+ spin_unlock(&c->orphan_lock);
}
/**
@@ -630,6 +628,7 @@ static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
{
struct ubifs_scan_node *snod;
struct ubifs_orph_node *orph;
+ struct ubifs_ino_node *ino = NULL;
unsigned long long cmt_no;
ino_t inum;
int i, n, err, first = 1;
@@ -676,23 +675,40 @@ static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
if (first)
first = 0;
+ ino = kmalloc(UBIFS_MAX_INO_NODE_SZ, GFP_NOFS);
+ if (!ino)
+ return -ENOMEM;
+
n = (le32_to_cpu(orph->ch.len) - UBIFS_ORPH_NODE_SZ) >> 3;
for (i = 0; i < n; i++) {
union ubifs_key key1, key2;
inum = le64_to_cpu(orph->inos[i]);
- dbg_rcvry("deleting orphaned inode %lu",
- (unsigned long)inum);
-
- lowest_ino_key(c, &key1, inum);
- highest_ino_key(c, &key2, inum);
- err = ubifs_tnc_remove_range(c, &key1, &key2);
+ ino_key_init(c, &key1, inum);
+ err = ubifs_tnc_lookup(c, &key1, ino);
if (err)
- return err;
+ goto out_free;
+
+ /*
+ * Check whether an inode can really get deleted.
+ * linkat() with O_TMPFILE allows rebirth of an inode.
+ */
+ if (ino->nlink == 0) {
+ dbg_rcvry("deleting orphaned inode %lu",
+ (unsigned long)inum);
+
+ lowest_ino_key(c, &key1, inum);
+ highest_ino_key(c, &key2, inum);
+
+ err = ubifs_tnc_remove_range(c, &key1, &key2);
+ if (err)
+ goto out_ro;
+ }
+
err = insert_dead_orphan(c, inum);
if (err)
- return err;
+ goto out_free;
}
*last_cmt_no = cmt_no;
@@ -704,7 +720,15 @@ static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
*last_flagged = 0;
}
- return 0;
+ err = 0;
+out_free:
+ kfree(ino);
+ return err;
+
+out_ro:
+ ubifs_ro_mode(c, err);
+ kfree(ino);
+ return err;
}
/**
diff --git a/fs/ubifs/recovery.c b/fs/ubifs/recovery.c
index 3fc589881825..f116f7b3f9e5 100644
--- a/fs/ubifs/recovery.c
+++ b/fs/ubifs/recovery.c
@@ -818,7 +818,7 @@ static int get_cs_sqnum(struct ubifs_info *c, int lnum, int offs,
goto out_err;
}
if (cs_node->ch.node_type != UBIFS_CS_NODE) {
- ubifs_err(c, "Node a CS node, type is %d", cs_node->ch.node_type);
+ ubifs_err(c, "Not a CS node, type is %d", cs_node->ch.node_type);
goto out_err;
}
if (le64_to_cpu(cs_node->cmt_no) != c->cmt_no) {
diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c
index 12c2afdb5804..a551eb3e9b89 100644
--- a/fs/ubifs/sb.c
+++ b/fs/ubifs/sb.c
@@ -578,17 +578,26 @@ static int authenticate_sb_node(struct ubifs_info *c,
return -EINVAL;
}
- err = ubifs_hmac_wkm(c, hmac_wkm);
- if (err)
- return err;
-
- if (ubifs_check_hmac(c, hmac_wkm, sup->hmac_wkm)) {
- ubifs_err(c, "provided key does not fit");
- return -ENOKEY;
+ /*
+ * The super block node can either be authenticated by a HMAC or
+ * by a signature in a ubifs_sig_node directly following the
+ * super block node to support offline image creation.
+ */
+ if (ubifs_hmac_zero(c, sup->hmac)) {
+ err = ubifs_sb_verify_signature(c, sup);
+ } else {
+ err = ubifs_hmac_wkm(c, hmac_wkm);
+ if (err)
+ return err;
+ if (ubifs_check_hmac(c, hmac_wkm, sup->hmac_wkm)) {
+ ubifs_err(c, "provided key does not fit");
+ return -ENOKEY;
+ }
+ err = ubifs_node_verify_hmac(c, sup, sizeof(*sup),
+ offsetof(struct ubifs_sb_node,
+ hmac));
}
- err = ubifs_node_verify_hmac(c, sup, sizeof(*sup),
- offsetof(struct ubifs_sb_node, hmac));
if (err)
ubifs_err(c, "Failed to authenticate superblock: %d", err);
@@ -744,21 +753,16 @@ int ubifs_read_superblock(struct ubifs_info *c)
}
/* Automatically increase file system size to the maximum size */
- c->old_leb_cnt = c->leb_cnt;
if (c->leb_cnt < c->vi.size && c->leb_cnt < c->max_leb_cnt) {
+ int old_leb_cnt = c->leb_cnt;
+
c->leb_cnt = min_t(int, c->max_leb_cnt, c->vi.size);
- if (c->ro_mount)
- dbg_mnt("Auto resizing (ro) from %d LEBs to %d LEBs",
- c->old_leb_cnt, c->leb_cnt);
- else {
- dbg_mnt("Auto resizing (sb) from %d LEBs to %d LEBs",
- c->old_leb_cnt, c->leb_cnt);
- sup->leb_cnt = cpu_to_le32(c->leb_cnt);
- err = ubifs_write_sb_node(c, sup);
- if (err)
- goto out;
- c->old_leb_cnt = c->leb_cnt;
- }
+ sup->leb_cnt = cpu_to_le32(c->leb_cnt);
+
+ c->superblock_need_write = 1;
+
+ dbg_mnt("Auto resizing from %d LEBs to %d LEBs",
+ old_leb_cnt, c->leb_cnt);
}
c->log_bytes = (long long)c->log_lebs * c->leb_size;
@@ -916,9 +920,7 @@ int ubifs_fixup_free_space(struct ubifs_info *c)
c->space_fixup = 0;
sup->flags &= cpu_to_le32(~UBIFS_FLG_SPACE_FIXUP);
- err = ubifs_write_sb_node(c, sup);
- if (err)
- return err;
+ c->superblock_need_write = 1;
ubifs_msg(c, "free space fixup complete");
return err;
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index fd1977b568f0..2c0803b0ac3a 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -566,6 +566,8 @@ static int init_constants_early(struct ubifs_info *c)
c->ranges[UBIFS_AUTH_NODE].min_len = UBIFS_AUTH_NODE_SZ;
c->ranges[UBIFS_AUTH_NODE].max_len = UBIFS_AUTH_NODE_SZ +
UBIFS_MAX_HMAC_LEN;
+ c->ranges[UBIFS_SIG_NODE].min_len = UBIFS_SIG_NODE_SZ;
+ c->ranges[UBIFS_SIG_NODE].max_len = c->leb_size - UBIFS_SB_NODE_SZ;
c->ranges[UBIFS_INO_NODE].min_len = UBIFS_INO_NODE_SZ;
c->ranges[UBIFS_INO_NODE].max_len = UBIFS_MAX_INO_NODE_SZ;
@@ -1043,6 +1045,8 @@ static int ubifs_parse_options(struct ubifs_info *c, char *options,
c->mount_opts.compr_type = UBIFS_COMPR_LZO;
else if (!strcmp(name, "zlib"))
c->mount_opts.compr_type = UBIFS_COMPR_ZLIB;
+ else if (!strcmp(name, "zstd"))
+ c->mount_opts.compr_type = UBIFS_COMPR_ZSTD;
else {
ubifs_err(c, "unknown compressor \"%s\"", name); //FIXME: is c ready?
kfree(name);
@@ -1296,8 +1300,7 @@ static int mount_ubifs(struct ubifs_info *c)
if (err)
goto out_free;
- sz = ALIGN(c->max_idx_node_sz, c->min_io_size);
- sz = ALIGN(sz + c->max_idx_node_sz, c->min_io_size);
+ sz = ALIGN(c->max_idx_node_sz, c->min_io_size) * 2;
c->cbuf = kmalloc(sz, GFP_NOFS);
if (!c->cbuf) {
err = -ENOMEM;
@@ -1360,6 +1363,26 @@ static int mount_ubifs(struct ubifs_info *c)
goto out_lpt;
}
+ /*
+ * Handle offline signed images: Now that the master node is
+ * written and its validation no longer depends on the hash
+ * in the superblock, we can update the offline signed
+ * superblock with a HMAC version,
+ */
+ if (ubifs_authenticated(c) && ubifs_hmac_zero(c, c->sup_node->hmac)) {
+ err = ubifs_hmac_wkm(c, c->sup_node->hmac_wkm);
+ if (err)
+ goto out_lpt;
+ c->superblock_need_write = 1;
+ }
+
+ if (!c->ro_mount && c->superblock_need_write) {
+ err = ubifs_write_sb_node(c, c->sup_node);
+ if (err)
+ goto out_lpt;
+ c->superblock_need_write = 0;
+ }
+
err = dbg_check_idx_size(c, c->bi.old_idx_sz);
if (err)
goto out_lpt;
@@ -1642,15 +1665,6 @@ static int ubifs_remount_rw(struct ubifs_info *c)
if (err)
goto out;
- if (c->old_leb_cnt != c->leb_cnt) {
- struct ubifs_sb_node *sup = c->sup_node;
-
- sup->leb_cnt = cpu_to_le32(c->leb_cnt);
- err = ubifs_write_sb_node(c, sup);
- if (err)
- goto out;
- }
-
if (c->need_recovery) {
ubifs_msg(c, "completing deferred recovery");
err = ubifs_write_rcvrd_mst_node(c);
@@ -1682,6 +1696,16 @@ static int ubifs_remount_rw(struct ubifs_info *c)
goto out;
}
+ if (c->superblock_need_write) {
+ struct ubifs_sb_node *sup = c->sup_node;
+
+ err = ubifs_write_sb_node(c, sup);
+ if (err)
+ goto out;
+
+ c->superblock_need_write = 0;
+ }
+
c->ileb_buf = vmalloc(c->leb_size);
if (!c->ileb_buf) {
err = -ENOMEM;
diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c
index f5a823cb0e43..e8e7b0e9532e 100644
--- a/fs/ubifs/tnc.c
+++ b/fs/ubifs/tnc.c
@@ -1158,8 +1158,8 @@ static struct ubifs_znode *dirty_cow_bottom_up(struct ubifs_info *c,
* o exact match, i.e. the found zero-level znode contains key @key, then %1
* is returned and slot number of the matched branch is stored in @n;
* o not exact match, which means that zero-level znode does not contain
- * @key, then %0 is returned and slot number of the closest branch is stored
- * in @n;
+ * @key, then %0 is returned and slot number of the closest branch or %-1
+ * is stored in @n; In this case calling tnc_next() is mandatory.
* o @key is so small that it is even less than the lowest key of the
* leftmost zero-level node, then %0 is returned and %0 is stored in @n.
*
@@ -1882,13 +1882,19 @@ int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key,
static int search_dh_cookie(struct ubifs_info *c, const union ubifs_key *key,
struct ubifs_dent_node *dent, uint32_t cookie,
- struct ubifs_znode **zn, int *n)
+ struct ubifs_znode **zn, int *n, int exact)
{
int err;
struct ubifs_znode *znode = *zn;
struct ubifs_zbranch *zbr;
union ubifs_key *dkey;
+ if (!exact) {
+ err = tnc_next(c, &znode, n);
+ if (err)
+ return err;
+ }
+
for (;;) {
zbr = &znode->zbranch[*n];
dkey = &zbr->key;
@@ -1930,7 +1936,7 @@ static int do_lookup_dh(struct ubifs_info *c, const union ubifs_key *key,
if (unlikely(err < 0))
goto out_unlock;
- err = search_dh_cookie(c, key, dent, cookie, &znode, &n);
+ err = search_dh_cookie(c, key, dent, cookie, &znode, &n, err);
out_unlock:
mutex_unlock(&c->tnc_mutex);
@@ -2723,7 +2729,7 @@ int ubifs_tnc_remove_dh(struct ubifs_info *c, const union ubifs_key *key,
if (unlikely(err < 0))
goto out_free;
- err = search_dh_cookie(c, key, dent, cookie, &znode, &n);
+ err = search_dh_cookie(c, key, dent, cookie, &znode, &n, err);
if (err)
goto out_free;
}
diff --git a/fs/ubifs/ubifs-media.h b/fs/ubifs/ubifs-media.h
index 86f0f2be116c..3c9792cbb6ff 100644
--- a/fs/ubifs/ubifs-media.h
+++ b/fs/ubifs/ubifs-media.h
@@ -275,6 +275,8 @@ enum {
#define UBIFS_CS_NODE_SZ sizeof(struct ubifs_cs_node)
#define UBIFS_ORPH_NODE_SZ sizeof(struct ubifs_orph_node)
#define UBIFS_AUTH_NODE_SZ sizeof(struct ubifs_auth_node)
+#define UBIFS_SIG_NODE_SZ sizeof(struct ubifs_sig_node)
+
/* Extended attribute entry nodes are identical to directory entry nodes */
#define UBIFS_XENT_NODE_SZ UBIFS_DENT_NODE_SZ
/* Only this does not have to be multiple of 8 bytes */
@@ -301,6 +303,8 @@ enum {
*/
#define UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT "c"
+/* Type field in ubifs_sig_node */
+#define UBIFS_SIGNATURE_TYPE_PKCS7 1
/*
* On-flash inode flags.
@@ -336,12 +340,14 @@ enum {
* UBIFS_COMPR_NONE: no compression
* UBIFS_COMPR_LZO: LZO compression
* UBIFS_COMPR_ZLIB: ZLIB compression
+ * UBIFS_COMPR_ZSTD: ZSTD compression
* UBIFS_COMPR_TYPES_CNT: count of supported compression types
*/
enum {
UBIFS_COMPR_NONE,
UBIFS_COMPR_LZO,
UBIFS_COMPR_ZLIB,
+ UBIFS_COMPR_ZSTD,
UBIFS_COMPR_TYPES_CNT,
};
@@ -361,6 +367,7 @@ enum {
* UBIFS_CS_NODE: commit start node
* UBIFS_ORPH_NODE: orphan node
* UBIFS_AUTH_NODE: authentication node
+ * UBIFS_SIG_NODE: signature node
* UBIFS_NODE_TYPES_CNT: count of supported node types
*
* Note, we index arrays by these numbers, so keep them low and contiguous.
@@ -381,6 +388,7 @@ enum {
UBIFS_CS_NODE,
UBIFS_ORPH_NODE,
UBIFS_AUTH_NODE,
+ UBIFS_SIG_NODE,
UBIFS_NODE_TYPES_CNT,
};
@@ -638,6 +646,8 @@ struct ubifs_pad_node {
* @hmac_wkm: HMAC of a well known message (the string "UBIFS") as a convenience
* to the user to check if the correct key is passed.
* @hash_algo: The hash algo used for this filesystem (one of enum hash_algo)
+ * @hash_mst: hash of the master node, only valid for signed images in which the
+ * master node does not contain a hmac
*/
struct ubifs_sb_node {
struct ubifs_ch ch;
@@ -668,7 +678,8 @@ struct ubifs_sb_node {
__u8 hmac[UBIFS_MAX_HMAC_LEN];
__u8 hmac_wkm[UBIFS_MAX_HMAC_LEN];
__le16 hash_algo;
- __u8 padding2[3838];
+ __u8 hash_mst[UBIFS_MAX_HASH_LEN];
+ __u8 padding2[3774];
} __packed;
/**
@@ -771,6 +782,23 @@ struct ubifs_auth_node {
} __packed;
/**
+ * struct ubifs_sig_node - node for signing other nodes
+ * @ch: common header
+ * @type: type of the signature, currently only UBIFS_SIGNATURE_TYPE_PKCS7
+ * supported
+ * @len: The length of the signature data
+ * @padding: reserved for future, zeroes
+ * @sig: The signature data
+ */
+struct ubifs_sig_node {
+ struct ubifs_ch ch;
+ __le32 type;
+ __le32 len;
+ __u8 padding[32];
+ __u8 sig[];
+} __packed;
+
+/**
* struct ubifs_branch - key/reference/length branch
* @lnum: LEB number of the target node
* @offs: offset within @lnum
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 745b23e5b406..c55f212dcb75 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -1104,7 +1104,6 @@ struct ubifs_debug_info;
* used to store indexing nodes (@leb_size - @max_idx_node_sz)
* @leb_cnt: count of logical eraseblocks
* @max_leb_cnt: maximum count of logical eraseblocks
- * @old_leb_cnt: count of logical eraseblocks before re-size
* @ro_media: the underlying UBI volume is read-only
* @ro_mount: the file-system was mounted as read-only
* @ro_error: UBIFS switched to R/O mode because an error happened
@@ -1295,6 +1294,7 @@ struct ubifs_info {
unsigned int rw_incompat:1;
unsigned int assert_action:2;
unsigned int authenticated:1;
+ unsigned int superblock_need_write:1;
struct mutex tnc_mutex;
struct ubifs_zbranch zroot;
@@ -1352,7 +1352,6 @@ struct ubifs_info {
int idx_leb_size;
int leb_cnt;
int max_leb_cnt;
- int old_leb_cnt;
unsigned int ro_media:1;
unsigned int ro_mount:1;
unsigned int ro_error:1;
@@ -1680,6 +1679,9 @@ static inline int ubifs_auth_node_sz(const struct ubifs_info *c)
else
return 0;
}
+int ubifs_sb_verify_signature(struct ubifs_info *c,
+ const struct ubifs_sb_node *sup);
+bool ubifs_hmac_zero(struct ubifs_info *c, const u8 *hmac);
int ubifs_hmac_wkm(struct ubifs_info *c, u8 *hmac);
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 451e7b544342..a95cce5e82e7 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -374,6 +374,7 @@ extern acpi_status wmi_install_notify_handler(const char *guid,
extern acpi_status wmi_remove_notify_handler(const char *guid);
extern acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out);
extern bool wmi_has_guid(const char *guid);
+extern char *wmi_get_acpi_device_uid(const char *guid);
#endif /* CONFIG_ACPI_WMI */
diff --git a/include/linux/hmm.h b/include/linux/hmm.h
index 044a36d7c3f8..b8a08b2a10ca 100644
--- a/include/linux/hmm.h
+++ b/include/linux/hmm.h
@@ -21,8 +21,8 @@
*
* HMM address space mirroring API:
*
- * Use HMM address space mirroring if you want to mirror range of the CPU page
- * table of a process into a device page table. Here, "mirror" means "keep
+ * Use HMM address space mirroring if you want to mirror a range of the CPU
+ * page tables of a process into a device page table. Here, "mirror" means "keep
* synchronized". Prerequisites: the device must provide the ability to write-
* protect its page tables (at PAGE_SIZE granularity), and must be able to
* recover from the resulting potential page faults.
@@ -62,7 +62,7 @@
#include <linux/kconfig.h>
#include <asm/pgtable.h>
-#if IS_ENABLED(CONFIG_HMM)
+#ifdef CONFIG_HMM_MIRROR
#include <linux/device.h>
#include <linux/migrate.h>
@@ -82,19 +82,18 @@
* @mirrors_sem: read/write semaphore protecting the mirrors list
* @wq: wait queue for user waiting on a range invalidation
* @notifiers: count of active mmu notifiers
- * @dead: is the mm dead ?
*/
struct hmm {
struct mm_struct *mm;
struct kref kref;
- struct mutex lock;
+ spinlock_t ranges_lock;
struct list_head ranges;
struct list_head mirrors;
struct mmu_notifier mmu_notifier;
struct rw_semaphore mirrors_sem;
wait_queue_head_t wq;
+ struct rcu_head rcu;
long notifiers;
- bool dead;
};
/*
@@ -105,10 +104,11 @@ struct hmm {
* HMM_PFN_WRITE: CPU page table has write permission set
* HMM_PFN_DEVICE_PRIVATE: private device memory (ZONE_DEVICE)
*
- * The driver provide a flags array, if driver valid bit for an entry is bit
- * 3 ie (entry & (1 << 3)) is true if entry is valid then driver must provide
+ * The driver provides a flags array for mapping page protections to device
+ * PTE bits. If the driver valid bit for an entry is bit 3,
+ * i.e., (entry & (1 << 3)), then the driver must provide
* an array in hmm_range.flags with hmm_range.flags[HMM_PFN_VALID] == 1 << 3.
- * Same logic apply to all flags. This is same idea as vm_page_prot in vma
+ * Same logic apply to all flags. This is the same idea as vm_page_prot in vma
* except that this is per device driver rather than per architecture.
*/
enum hmm_pfn_flag_e {
@@ -129,13 +129,13 @@ enum hmm_pfn_flag_e {
* be mirrored by a device, because the entry will never have HMM_PFN_VALID
* set and the pfn value is undefined.
*
- * Driver provide entry value for none entry, error entry and special entry,
- * driver can alias (ie use same value for error and special for instance). It
- * should not alias none and error or special.
+ * Driver provides values for none entry, error entry, and special entry.
+ * Driver can alias (i.e., use same value) error and special, but
+ * it should not alias none with error or special.
*
* HMM pfn value returned by hmm_vma_get_pfns() or hmm_vma_fault() will be:
* hmm_range.values[HMM_PFN_ERROR] if CPU page table entry is poisonous,
- * hmm_range.values[HMM_PFN_NONE] if there is no CPU page table
+ * hmm_range.values[HMM_PFN_NONE] if there is no CPU page table entry,
* hmm_range.values[HMM_PFN_SPECIAL] if CPU page table entry is a special one
*/
enum hmm_pfn_value_e {
@@ -158,6 +158,7 @@ enum hmm_pfn_value_e {
* @values: pfn value for some special case (none, special, error, ...)
* @default_flags: default flags for the range (write, read, ... see hmm doc)
* @pfn_flags_mask: allows to mask pfn flags so that only default_flags matter
+ * @page_shift: device virtual address shift value (should be >= PAGE_SHIFT)
* @pfn_shifts: pfn shift value (should be <= PAGE_SHIFT)
* @valid: pfns array did not change since it has been fill by an HMM function
*/
@@ -180,7 +181,7 @@ struct hmm_range {
/*
* hmm_range_page_shift() - return the page shift for the range
* @range: range being queried
- * Returns: page shift (page size = 1 << page shift) for the range
+ * Return: page shift (page size = 1 << page shift) for the range
*/
static inline unsigned hmm_range_page_shift(const struct hmm_range *range)
{
@@ -190,7 +191,7 @@ static inline unsigned hmm_range_page_shift(const struct hmm_range *range)
/*
* hmm_range_page_size() - return the page size for the range
* @range: range being queried
- * Returns: page size for the range in bytes
+ * Return: page size for the range in bytes
*/
static inline unsigned long hmm_range_page_size(const struct hmm_range *range)
{
@@ -201,28 +202,19 @@ static inline unsigned long hmm_range_page_size(const struct hmm_range *range)
* hmm_range_wait_until_valid() - wait for range to be valid
* @range: range affected by invalidation to wait on
* @timeout: time out for wait in ms (ie abort wait after that period of time)
- * Returns: true if the range is valid, false otherwise.
+ * Return: true if the range is valid, false otherwise.
*/
static inline bool hmm_range_wait_until_valid(struct hmm_range *range,
unsigned long timeout)
{
- /* Check if mm is dead ? */
- if (range->hmm == NULL || range->hmm->dead || range->hmm->mm == NULL) {
- range->valid = false;
- return false;
- }
- if (range->valid)
- return true;
- wait_event_timeout(range->hmm->wq, range->valid || range->hmm->dead,
- msecs_to_jiffies(timeout));
- /* Return current valid status just in case we get lucky */
- return range->valid;
+ return wait_event_timeout(range->hmm->wq, range->valid,
+ msecs_to_jiffies(timeout)) != 0;
}
/*
* hmm_range_valid() - test if a range is valid or not
* @range: range
- * Returns: true if the range is valid, false otherwise.
+ * Return: true if the range is valid, false otherwise.
*/
static inline bool hmm_range_valid(struct hmm_range *range)
{
@@ -233,7 +225,7 @@ static inline bool hmm_range_valid(struct hmm_range *range)
* hmm_device_entry_to_page() - return struct page pointed to by a device entry
* @range: range use to decode device entry value
* @entry: device entry value to get corresponding struct page from
- * Returns: struct page pointer if entry is a valid, NULL otherwise
+ * Return: struct page pointer if entry is a valid, NULL otherwise
*
* If the device entry is valid (ie valid flag set) then return the struct page
* matching the entry value. Otherwise return NULL.
@@ -256,7 +248,7 @@ static inline struct page *hmm_device_entry_to_page(const struct hmm_range *rang
* hmm_device_entry_to_pfn() - return pfn value store in a device entry
* @range: range use to decode device entry value
* @entry: device entry to extract pfn from
- * Returns: pfn value if device entry is valid, -1UL otherwise
+ * Return: pfn value if device entry is valid, -1UL otherwise
*/
static inline unsigned long
hmm_device_entry_to_pfn(const struct hmm_range *range, uint64_t pfn)
@@ -276,7 +268,7 @@ hmm_device_entry_to_pfn(const struct hmm_range *range, uint64_t pfn)
* hmm_device_entry_from_page() - create a valid device entry for a page
* @range: range use to encode HMM pfn value
* @page: page for which to create the device entry
- * Returns: valid device entry for the page
+ * Return: valid device entry for the page
*/
static inline uint64_t hmm_device_entry_from_page(const struct hmm_range *range,
struct page *page)
@@ -289,7 +281,7 @@ static inline uint64_t hmm_device_entry_from_page(const struct hmm_range *range,
* hmm_device_entry_from_pfn() - create a valid device entry value from pfn
* @range: range use to encode HMM pfn value
* @pfn: pfn value for which to create the device entry
- * Returns: valid device entry for the pfn
+ * Return: valid device entry for the pfn
*/
static inline uint64_t hmm_device_entry_from_pfn(const struct hmm_range *range,
unsigned long pfn)
@@ -332,9 +324,6 @@ static inline uint64_t hmm_pfn_from_pfn(const struct hmm_range *range,
return hmm_device_entry_from_pfn(range, pfn);
}
-
-
-#if IS_ENABLED(CONFIG_HMM_MIRROR)
/*
* Mirroring: how to synchronize device page table with CPU page table.
*
@@ -394,7 +383,7 @@ enum hmm_update_event {
};
/*
- * struct hmm_update - HMM update informations for callback
+ * struct hmm_update - HMM update information for callback
*
* @start: virtual start address of the range to update
* @end: virtual end address of the range to update
@@ -418,17 +407,18 @@ struct hmm_mirror_ops {
*
* @mirror: pointer to struct hmm_mirror
*
- * This is called when the mm_struct is being released.
- * The callback should make sure no references to the mirror occur
- * after the callback returns.
+ * This is called when the mm_struct is being released. The callback
+ * must ensure that all access to any pages obtained from this mirror
+ * is halted before the callback returns. All future access should
+ * fault.
*/
void (*release)(struct hmm_mirror *mirror);
/* sync_cpu_device_pagetables() - synchronize page tables
*
* @mirror: pointer to struct hmm_mirror
- * @update: update informations (see struct hmm_update)
- * Returns: -EAGAIN if update.blockable false and callback need to
+ * @update: update information (see struct hmm_update)
+ * Return: -EAGAIN if update.blockable false and callback need to
* block, 0 otherwise.
*
* This callback ultimately originates from mmu_notifiers when the CPU
@@ -465,35 +455,10 @@ int hmm_mirror_register(struct hmm_mirror *mirror, struct mm_struct *mm);
void hmm_mirror_unregister(struct hmm_mirror *mirror);
/*
- * hmm_mirror_mm_is_alive() - test if mm is still alive
- * @mirror: the HMM mm mirror for which we want to lock the mmap_sem
- * Returns: false if the mm is dead, true otherwise
- *
- * This is an optimization it will not accurately always return -EINVAL if the
- * mm is dead ie there can be false negative (process is being kill but HMM is
- * not yet inform of that). It is only intented to be use to optimize out case
- * where driver is about to do something time consuming and it would be better
- * to skip it if the mm is dead.
- */
-static inline bool hmm_mirror_mm_is_alive(struct hmm_mirror *mirror)
-{
- struct mm_struct *mm;
-
- if (!mirror || !mirror->hmm)
- return false;
- mm = READ_ONCE(mirror->hmm->mm);
- if (mirror->hmm->dead || !mm)
- return false;
-
- return true;
-}
-
-
-/*
* Please see Documentation/vm/hmm.rst for how to use the range API.
*/
int hmm_range_register(struct hmm_range *range,
- struct mm_struct *mm,
+ struct hmm_mirror *mirror,
unsigned long start,
unsigned long end,
unsigned page_shift);
@@ -529,7 +494,8 @@ static inline bool hmm_vma_range_done(struct hmm_range *range)
}
/* This is a temporary helper to avoid merge conflict between trees. */
-static inline int hmm_vma_fault(struct hmm_range *range, bool block)
+static inline int hmm_vma_fault(struct hmm_mirror *mirror,
+ struct hmm_range *range, bool block)
{
long ret;
@@ -542,7 +508,7 @@ static inline int hmm_vma_fault(struct hmm_range *range, bool block)
range->default_flags = 0;
range->pfn_flags_mask = -1UL;
- ret = hmm_range_register(range, range->vma->vm_mm,
+ ret = hmm_range_register(range, mirror,
range->start, range->end,
PAGE_SHIFT);
if (ret)
@@ -561,7 +527,7 @@ static inline int hmm_vma_fault(struct hmm_range *range, bool block)
ret = hmm_range_fault(range, block);
if (ret <= 0) {
if (ret == -EBUSY || !ret) {
- /* Same as above drop mmap_sem to match old API. */
+ /* Same as above, drop mmap_sem to match old API. */
up_read(&range->vma->vm_mm->mmap_sem);
ret = -EBUSY;
} else if (ret == -EAGAIN)
@@ -573,208 +539,12 @@ static inline int hmm_vma_fault(struct hmm_range *range, bool block)
}
/* Below are for HMM internal use only! Not to be used by device driver! */
-void hmm_mm_destroy(struct mm_struct *mm);
-
static inline void hmm_mm_init(struct mm_struct *mm)
{
mm->hmm = NULL;
}
#else /* IS_ENABLED(CONFIG_HMM_MIRROR) */
-static inline void hmm_mm_destroy(struct mm_struct *mm) {}
static inline void hmm_mm_init(struct mm_struct *mm) {}
#endif /* IS_ENABLED(CONFIG_HMM_MIRROR) */
-#if IS_ENABLED(CONFIG_DEVICE_PRIVATE) || IS_ENABLED(CONFIG_DEVICE_PUBLIC)
-struct hmm_devmem;
-
-struct page *hmm_vma_alloc_locked_page(struct vm_area_struct *vma,
- unsigned long addr);
-
-/*
- * struct hmm_devmem_ops - callback for ZONE_DEVICE memory events
- *
- * @free: call when refcount on page reach 1 and thus is no longer use
- * @fault: call when there is a page fault to unaddressable memory
- *
- * Both callback happens from page_free() and page_fault() callback of struct
- * dev_pagemap respectively. See include/linux/memremap.h for more details on
- * those.
- *
- * The hmm_devmem_ops callback are just here to provide a coherent and
- * uniq API to device driver and device driver should not register their
- * own page_free() or page_fault() but rely on the hmm_devmem_ops call-
- * back.
- */
-struct hmm_devmem_ops {
- /*
- * free() - free a device page
- * @devmem: device memory structure (see struct hmm_devmem)
- * @page: pointer to struct page being freed
- *
- * Call back occurs whenever a device page refcount reach 1 which
- * means that no one is holding any reference on the page anymore
- * (ZONE_DEVICE page have an elevated refcount of 1 as default so
- * that they are not release to the general page allocator).
- *
- * Note that callback has exclusive ownership of the page (as no
- * one is holding any reference).
- */
- void (*free)(struct hmm_devmem *devmem, struct page *page);
- /*
- * fault() - CPU page fault or get user page (GUP)
- * @devmem: device memory structure (see struct hmm_devmem)
- * @vma: virtual memory area containing the virtual address
- * @addr: virtual address that faulted or for which there is a GUP
- * @page: pointer to struct page backing virtual address (unreliable)
- * @flags: FAULT_FLAG_* (see include/linux/mm.h)
- * @pmdp: page middle directory
- * Returns: VM_FAULT_MINOR/MAJOR on success or one of VM_FAULT_ERROR
- * on error
- *
- * The callback occurs whenever there is a CPU page fault or GUP on a
- * virtual address. This means that the device driver must migrate the
- * page back to regular memory (CPU accessible).
- *
- * The device driver is free to migrate more than one page from the
- * fault() callback as an optimization. However if device decide to
- * migrate more than one page it must always priotirize the faulting
- * address over the others.
- *
- * The struct page pointer is only given as an hint to allow quick
- * lookup of internal device driver data. A concurrent migration
- * might have already free that page and the virtual address might
- * not longer be back by it. So it should not be modified by the
- * callback.
- *
- * Note that mmap semaphore is held in read mode at least when this
- * callback occurs, hence the vma is valid upon callback entry.
- */
- vm_fault_t (*fault)(struct hmm_devmem *devmem,
- struct vm_area_struct *vma,
- unsigned long addr,
- const struct page *page,
- unsigned int flags,
- pmd_t *pmdp);
-};
-
-/*
- * struct hmm_devmem - track device memory
- *
- * @completion: completion object for device memory
- * @pfn_first: first pfn for this resource (set by hmm_devmem_add())
- * @pfn_last: last pfn for this resource (set by hmm_devmem_add())
- * @resource: IO resource reserved for this chunk of memory
- * @pagemap: device page map for that chunk
- * @device: device to bind resource to
- * @ops: memory operations callback
- * @ref: per CPU refcount
- * @page_fault: callback when CPU fault on an unaddressable device page
- *
- * This an helper structure for device drivers that do not wish to implement
- * the gory details related to hotplugging new memoy and allocating struct
- * pages.
- *
- * Device drivers can directly use ZONE_DEVICE memory on their own if they
- * wish to do so.
- *
- * The page_fault() callback must migrate page back, from device memory to
- * system memory, so that the CPU can access it. This might fail for various
- * reasons (device issues, device have been unplugged, ...). When such error
- * conditions happen, the page_fault() callback must return VM_FAULT_SIGBUS and
- * set the CPU page table entry to "poisoned".
- *
- * Note that because memory cgroup charges are transferred to the device memory,
- * this should never fail due to memory restrictions. However, allocation
- * of a regular system page might still fail because we are out of memory. If
- * that happens, the page_fault() callback must return VM_FAULT_OOM.
- *
- * The page_fault() callback can also try to migrate back multiple pages in one
- * chunk, as an optimization. It must, however, prioritize the faulting address
- * over all the others.
- */
-typedef vm_fault_t (*dev_page_fault_t)(struct vm_area_struct *vma,
- unsigned long addr,
- const struct page *page,
- unsigned int flags,
- pmd_t *pmdp);
-
-struct hmm_devmem {
- struct completion completion;
- unsigned long pfn_first;
- unsigned long pfn_last;
- struct resource *resource;
- struct device *device;
- struct dev_pagemap pagemap;
- const struct hmm_devmem_ops *ops;
- struct percpu_ref ref;
- dev_page_fault_t page_fault;
-};
-
-/*
- * To add (hotplug) device memory, HMM assumes that there is no real resource
- * that reserves a range in the physical address space (this is intended to be
- * use by unaddressable device memory). It will reserve a physical range big
- * enough and allocate struct page for it.
- *
- * The device driver can wrap the hmm_devmem struct inside a private device
- * driver struct.
- */
-struct hmm_devmem *hmm_devmem_add(const struct hmm_devmem_ops *ops,
- struct device *device,
- unsigned long size);
-struct hmm_devmem *hmm_devmem_add_resource(const struct hmm_devmem_ops *ops,
- struct device *device,
- struct resource *res);
-
-/*
- * hmm_devmem_page_set_drvdata - set per-page driver data field
- *
- * @page: pointer to struct page
- * @data: driver data value to set
- *
- * Because page can not be on lru we have an unsigned long that driver can use
- * to store a per page field. This just a simple helper to do that.
- */
-static inline void hmm_devmem_page_set_drvdata(struct page *page,
- unsigned long data)
-{
- page->hmm_data = data;
-}
-
-/*
- * hmm_devmem_page_get_drvdata - get per page driver data field
- *
- * @page: pointer to struct page
- * Return: driver data value
- */
-static inline unsigned long hmm_devmem_page_get_drvdata(const struct page *page)
-{
- return page->hmm_data;
-}
-
-
-/*
- * struct hmm_device - fake device to hang device memory onto
- *
- * @device: device struct
- * @minor: device minor number
- */
-struct hmm_device {
- struct device device;
- unsigned int minor;
-};
-
-/*
- * A device driver that wants to handle multiple devices memory through a
- * single fake device can use hmm_device to do so. This is purely a helper and
- * it is not strictly needed, in order to make use of any HMM functionality.
- */
-struct hmm_device *hmm_device_new(void *drvdata);
-void hmm_device_put(struct hmm_device *hmm_device);
-#endif /* CONFIG_DEVICE_PRIVATE || CONFIG_DEVICE_PUBLIC */
-#else /* IS_ENABLED(CONFIG_HMM) */
-static inline void hmm_mm_destroy(struct mm_struct *mm) {}
-static inline void hmm_mm_init(struct mm_struct *mm) {}
-#endif /* IS_ENABLED(CONFIG_HMM) */
-
#endif /* LINUX_HMM_H */
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index 5db386cfc2d4..5b6a7121c9f0 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -133,8 +133,7 @@ enum {
IORES_DESC_PERSISTENT_MEMORY = 4,
IORES_DESC_PERSISTENT_MEMORY_LEGACY = 5,
IORES_DESC_DEVICE_PRIVATE_MEMORY = 6,
- IORES_DESC_DEVICE_PUBLIC_MEMORY = 7,
- IORES_DESC_RESERVED = 8,
+ IORES_DESC_RESERVED = 7,
};
/*
@@ -296,6 +295,8 @@ static inline bool resource_overlaps(struct resource *r1, struct resource *r2)
return (r1->start <= r2->end && r1->end >= r2->start);
}
+struct resource *devm_request_free_mem_region(struct device *dev,
+ struct resource *base, unsigned long size);
#endif /* __ASSEMBLY__ */
#endif /* _LINUX_IOPORT_H */
diff --git a/include/linux/memremap.h b/include/linux/memremap.h
index 1732dea030b2..f8a5b2a19945 100644
--- a/include/linux/memremap.h
+++ b/include/linux/memremap.h
@@ -37,13 +37,6 @@ struct vmem_altmap {
* A more complete discussion of unaddressable memory may be found in
* include/linux/hmm.h and Documentation/vm/hmm.rst.
*
- * MEMORY_DEVICE_PUBLIC:
- * Device memory that is cache coherent from device and CPU point of view. This
- * is use on platform that have an advance system bus (like CAPI or CCIX). A
- * driver can hotplug the device memory using ZONE_DEVICE and with that memory
- * type. Any page of a process can be migrated to such memory. However no one
- * should be allow to pin such memory so that it can always be evicted.
- *
* MEMORY_DEVICE_FS_DAX:
* Host memory that has similar access semantics as System RAM i.e. DMA
* coherent and supports page pinning. In support of coordinating page
@@ -52,54 +45,84 @@ struct vmem_altmap {
* wakeup is used to coordinate physical address space management (ex:
* fs truncate/hole punch) vs pinned pages (ex: device dma).
*
+ * MEMORY_DEVICE_DEVDAX:
+ * Host memory that has similar access semantics as System RAM i.e. DMA
+ * coherent and supports page pinning. In contrast to
+ * MEMORY_DEVICE_FS_DAX, this memory is access via a device-dax
+ * character device.
+ *
* MEMORY_DEVICE_PCI_P2PDMA:
* Device memory residing in a PCI BAR intended for use with Peer-to-Peer
* transactions.
*/
enum memory_type {
+ /* 0 is reserved to catch uninitialized type fields */
MEMORY_DEVICE_PRIVATE = 1,
- MEMORY_DEVICE_PUBLIC,
MEMORY_DEVICE_FS_DAX,
+ MEMORY_DEVICE_DEVDAX,
MEMORY_DEVICE_PCI_P2PDMA,
};
-/*
- * Additional notes about MEMORY_DEVICE_PRIVATE may be found in
- * include/linux/hmm.h and Documentation/vm/hmm.rst. There is also a brief
- * explanation in include/linux/memory_hotplug.h.
- *
- * The page_free() callback is called once the page refcount reaches 1
- * (ZONE_DEVICE pages never reach 0 refcount unless there is a refcount bug.
- * This allows the device driver to implement its own memory management.)
- */
-typedef void (*dev_page_free_t)(struct page *page, void *data);
+struct dev_pagemap_ops {
+ /*
+ * Called once the page refcount reaches 1. (ZONE_DEVICE pages never
+ * reach 0 refcount unless there is a refcount bug. This allows the
+ * device driver to implement its own memory management.)
+ */
+ void (*page_free)(struct page *page);
+
+ /*
+ * Transition the refcount in struct dev_pagemap to the dead state.
+ */
+ void (*kill)(struct dev_pagemap *pgmap);
+
+ /*
+ * Wait for refcount in struct dev_pagemap to be idle and reap it.
+ */
+ void (*cleanup)(struct dev_pagemap *pgmap);
+
+ /*
+ * Used for private (un-addressable) device memory only. Must migrate
+ * the page back to a CPU accessible page.
+ */
+ vm_fault_t (*migrate_to_ram)(struct vm_fault *vmf);
+};
+
+#define PGMAP_ALTMAP_VALID (1 << 0)
/**
* struct dev_pagemap - metadata for ZONE_DEVICE mappings
- * @page_free: free page callback when page refcount reaches 1
* @altmap: pre-allocated/reserved memory for vmemmap allocations
* @res: physical address range covered by @ref
* @ref: reference count that pins the devm_memremap_pages() mapping
- * @kill: callback to transition @ref to the dead state
- * @cleanup: callback to wait for @ref to be idle and reap it
+ * @internal_ref: internal reference if @ref is not provided by the caller
+ * @done: completion for @internal_ref
* @dev: host device of the mapping for debug
* @data: private data pointer for page_free()
* @type: memory type: see MEMORY_* in memory_hotplug.h
+ * @flags: PGMAP_* flags to specify defailed behavior
+ * @ops: method table
*/
struct dev_pagemap {
- dev_page_free_t page_free;
struct vmem_altmap altmap;
- bool altmap_valid;
struct resource res;
struct percpu_ref *ref;
- void (*kill)(struct percpu_ref *ref);
- void (*cleanup)(struct percpu_ref *ref);
+ struct percpu_ref internal_ref;
+ struct completion done;
struct device *dev;
- void *data;
enum memory_type type;
+ unsigned int flags;
u64 pci_p2pdma_bus_offset;
+ const struct dev_pagemap_ops *ops;
};
+static inline struct vmem_altmap *pgmap_altmap(struct dev_pagemap *pgmap)
+{
+ if (pgmap->flags & PGMAP_ALTMAP_VALID)
+ return &pgmap->altmap;
+ return NULL;
+}
+
#ifdef CONFIG_ZONE_DEVICE
void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap);
void devm_memunmap_pages(struct device *dev, struct dev_pagemap *pgmap);
diff --git a/include/linux/mm.h b/include/linux/mm.h
index f88f0eabcc5e..0389c34ac529 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -937,8 +937,6 @@ static inline bool is_zone_device_page(const struct page *page)
#endif
#ifdef CONFIG_DEV_PAGEMAP_OPS
-void dev_pagemap_get_ops(void);
-void dev_pagemap_put_ops(void);
void __put_devmap_managed_page(struct page *page);
DECLARE_STATIC_KEY_FALSE(devmap_managed_key);
static inline bool put_devmap_managed_page(struct page *page)
@@ -949,7 +947,6 @@ static inline bool put_devmap_managed_page(struct page *page)
return false;
switch (page->pgmap->type) {
case MEMORY_DEVICE_PRIVATE:
- case MEMORY_DEVICE_PUBLIC:
case MEMORY_DEVICE_FS_DAX:
__put_devmap_managed_page(page);
return true;
@@ -965,12 +962,6 @@ static inline bool is_device_private_page(const struct page *page)
page->pgmap->type == MEMORY_DEVICE_PRIVATE;
}
-static inline bool is_device_public_page(const struct page *page)
-{
- return is_zone_device_page(page) &&
- page->pgmap->type == MEMORY_DEVICE_PUBLIC;
-}
-
#ifdef CONFIG_PCI_P2PDMA
static inline bool is_pci_p2pdma_page(const struct page *page)
{
@@ -985,14 +976,6 @@ static inline bool is_pci_p2pdma_page(const struct page *page)
#endif /* CONFIG_PCI_P2PDMA */
#else /* CONFIG_DEV_PAGEMAP_OPS */
-static inline void dev_pagemap_get_ops(void)
-{
-}
-
-static inline void dev_pagemap_put_ops(void)
-{
-}
-
static inline bool put_devmap_managed_page(struct page *page)
{
return false;
@@ -1003,11 +986,6 @@ static inline bool is_device_private_page(const struct page *page)
return false;
}
-static inline bool is_device_public_page(const struct page *page)
-{
- return false;
-}
-
static inline bool is_pci_p2pdma_page(const struct page *page)
{
return false;
@@ -1436,10 +1414,8 @@ struct zap_details {
pgoff_t last_index; /* Highest page->index to unmap */
};
-struct page *_vm_normal_page(struct vm_area_struct *vma, unsigned long addr,
- pte_t pte, bool with_public_device);
-#define vm_normal_page(vma, addr, pte) _vm_normal_page(vma, addr, pte, false)
-
+struct page *vm_normal_page(struct vm_area_struct *vma, unsigned long addr,
+ pte_t pte);
struct page *vm_normal_page_pmd(struct vm_area_struct *vma, unsigned long addr,
pmd_t pmd);
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 1d1093474c1a..3a37a89eb7a7 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -158,7 +158,7 @@ struct page {
struct { /* ZONE_DEVICE pages */
/** @pgmap: Points to the hosting device page map. */
struct dev_pagemap *pgmap;
- unsigned long hmm_data;
+ void *zone_device_data;
unsigned long _zd_pad_1; /* uses mapping */
};
@@ -503,7 +503,7 @@ struct mm_struct {
#endif
struct work_struct async_put_work;
-#if IS_ENABLED(CONFIG_HMM)
+#ifdef CONFIG_HMM_MIRROR
/* HMM needs to track a few things per mm */
struct hmm *hmm;
#endif
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index 448621c32e4d..09366859aac2 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -798,6 +798,7 @@ struct tee_client_device_id {
*/
struct wmi_device_id {
const char guid_string[UUID_STRING_LEN+1];
+ const void *context;
};
#endif /* LINUX_MOD_DEVICETABLE_H */
diff --git a/include/linux/olpc-ec.h b/include/linux/olpc-ec.h
index 79bdc6328c52..c4602364e909 100644
--- a/include/linux/olpc-ec.h
+++ b/include/linux/olpc-ec.h
@@ -2,6 +2,8 @@
#ifndef _LINUX_OLPC_EC_H
#define _LINUX_OLPC_EC_H
+#include <linux/bits.h>
+
/* XO-1 EC commands */
#define EC_FIRMWARE_REV 0x08
#define EC_WRITE_SCI_MASK 0x1b
@@ -16,28 +18,57 @@
#define EC_SCI_QUERY 0x84
#define EC_EXT_SCI_QUERY 0x85
+/* SCI source values */
+#define EC_SCI_SRC_GAME BIT(0)
+#define EC_SCI_SRC_BATTERY BIT(1)
+#define EC_SCI_SRC_BATSOC BIT(2)
+#define EC_SCI_SRC_BATERR BIT(3)
+#define EC_SCI_SRC_EBOOK BIT(4) /* XO-1 only */
+#define EC_SCI_SRC_WLAN BIT(5) /* XO-1 only */
+#define EC_SCI_SRC_ACPWR BIT(6)
+#define EC_SCI_SRC_BATCRIT BIT(7)
+#define EC_SCI_SRC_GPWAKE BIT(8) /* XO-1.5 only */
+#define EC_SCI_SRC_ALL GENMASK(8, 0)
+
struct platform_device;
struct olpc_ec_driver {
- int (*probe)(struct platform_device *);
int (*suspend)(struct platform_device *);
int (*resume)(struct platform_device *);
int (*ec_cmd)(u8, u8 *, size_t, u8 *, size_t, void *);
+
+ bool wakeup_available;
};
-#ifdef CONFIG_OLPC
+#ifdef CONFIG_OLPC_EC
extern void olpc_ec_driver_register(struct olpc_ec_driver *drv, void *arg);
extern int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf,
size_t outlen);
+extern void olpc_ec_wakeup_set(u16 value);
+extern void olpc_ec_wakeup_clear(u16 value);
+
+extern int olpc_ec_mask_write(u16 bits);
+extern int olpc_ec_sci_query(u16 *sci_value);
+
+extern bool olpc_ec_wakeup_available(void);
+
#else
static inline int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf,
size_t outlen) { return -ENODEV; }
-#endif /* CONFIG_OLPC */
+static inline void olpc_ec_wakeup_set(u16 value) { }
+static inline void olpc_ec_wakeup_clear(u16 value) { }
+
+static inline bool olpc_ec_wakeup_available(void)
+{
+ return false;
+}
+
+#endif /* CONFIG_OLPC_EC */
#endif /* _LINUX_OLPC_EC_H */
diff --git a/include/linux/omap-mailbox.h b/include/linux/omap-mailbox.h
index 6dbcd2da0332..8aa984ec1f38 100644
--- a/include/linux/omap-mailbox.h
+++ b/include/linux/omap-mailbox.h
@@ -6,7 +6,9 @@
#ifndef OMAP_MAILBOX_H
#define OMAP_MAILBOX_H
-typedef u32 mbox_msg_t;
+typedef uintptr_t mbox_msg_t;
+
+#define omap_mbox_message(data) (u32)(mbox_msg_t)(data)
typedef int __bitwise omap_mbox_irq_t;
#define IRQ_TX ((__force omap_mbox_irq_t) 1)
diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h
index b297cd1cd4f1..7aef0abc194a 100644
--- a/include/linux/percpu-refcount.h
+++ b/include/linux/percpu-refcount.h
@@ -75,14 +75,21 @@ enum {
* operation using percpu_ref_switch_to_percpu(). If initialized
* with this flag, the ref will stay in atomic mode until
* percpu_ref_switch_to_percpu() is invoked on it.
+ * Implies ALLOW_REINIT.
*/
PERCPU_REF_INIT_ATOMIC = 1 << 0,
/*
* Start dead w/ ref == 0 in atomic mode. Must be revived with
- * percpu_ref_reinit() before used. Implies INIT_ATOMIC.
+ * percpu_ref_reinit() before used. Implies INIT_ATOMIC and
+ * ALLOW_REINIT.
*/
PERCPU_REF_INIT_DEAD = 1 << 1,
+
+ /*
+ * Allow switching from atomic mode to percpu mode.
+ */
+ PERCPU_REF_ALLOW_REINIT = 1 << 2,
};
struct percpu_ref {
@@ -95,6 +102,7 @@ struct percpu_ref {
percpu_ref_func_t *release;
percpu_ref_func_t *confirm_switch;
bool force_atomic:1;
+ bool allow_reinit:1;
struct rcu_head rcu;
};
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 16e38c286d46..e8ad3c590a23 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -1055,6 +1055,11 @@ static inline int in_software_context(struct perf_event *event)
return event->ctx->pmu->task_ctx_nr == perf_sw_context;
}
+static inline int is_exclusive_pmu(struct pmu *pmu)
+{
+ return pmu->capabilities & PERF_PMU_CAP_EXCLUSIVE;
+}
+
extern struct static_key perf_swevent_enabled[PERF_COUNT_SW_MAX];
extern void ___perf_sw_event(u32, u64, struct pt_regs *, u64);
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
index bfba245636a7..8551156b8dca 100644
--- a/include/linux/platform_data/x86/asus-wmi.h
+++ b/include/linux/platform_data/x86/asus-wmi.h
@@ -18,8 +18,8 @@
#define ASUS_WMI_METHODID_GDSP 0x50534447 /* Get DiSPlay output */
#define ASUS_WMI_METHODID_DEVP 0x50564544 /* DEVice Policy */
#define ASUS_WMI_METHODID_OSVR 0x5256534F /* OS VeRsion */
-#define ASUS_WMI_METHODID_DSTS 0x53544344 /* Device STatuS */
-#define ASUS_WMI_METHODID_DSTS2 0x53545344 /* Device STatuS #2*/
+#define ASUS_WMI_METHODID_DCTS 0x53544344 /* Device status (DCTS) */
+#define ASUS_WMI_METHODID_DSTS 0x53545344 /* Device status (DSTS) */
#define ASUS_WMI_METHODID_BSTS 0x53545342 /* Bios STatuS ? */
#define ASUS_WMI_METHODID_DEVS 0x53564544 /* DEVice Set */
#define ASUS_WMI_METHODID_CFVS 0x53564643 /* CPU Frequency Volt Set */
@@ -57,6 +57,7 @@
#define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021
#define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */
#define ASUS_WMI_DEVID_LIGHTBAR 0x00050025
+#define ASUS_WMI_DEVID_FAN_MODE 0x00110018
/* Misc */
#define ASUS_WMI_DEVID_CAMERA 0x00060013
diff --git a/include/linux/swapops.h b/include/linux/swapops.h
index 4d961668e5fc..15bdb6fe71e5 100644
--- a/include/linux/swapops.h
+++ b/include/linux/swapops.h
@@ -129,12 +129,6 @@ static inline struct page *device_private_entry_to_page(swp_entry_t entry)
{
return pfn_to_page(swp_offset(entry));
}
-
-vm_fault_t device_private_entry_fault(struct vm_area_struct *vma,
- unsigned long addr,
- swp_entry_t entry,
- unsigned int flags,
- pmd_t *pmdp);
#else /* CONFIG_DEVICE_PRIVATE */
static inline swp_entry_t make_device_private_entry(struct page *page, bool write)
{
@@ -164,15 +158,6 @@ static inline struct page *device_private_entry_to_page(swp_entry_t entry)
{
return NULL;
}
-
-static inline vm_fault_t device_private_entry_fault(struct vm_area_struct *vma,
- unsigned long addr,
- swp_entry_t entry,
- unsigned int flags,
- pmd_t *pmdp)
-{
- return VM_FAULT_SIGBUS;
-}
#endif /* CONFIG_DEVICE_PRIVATE */
#ifdef CONFIG_MIGRATION
diff --git a/include/linux/wmi.h b/include/linux/wmi.h
index fcc9d029f67a..8ef7e7faea1e 100644
--- a/include/linux/wmi.h
+++ b/include/linux/wmi.h
@@ -36,7 +36,7 @@ struct wmi_driver {
struct device_driver driver;
const struct wmi_device_id *id_table;
- int (*probe)(struct wmi_device *wdev);
+ int (*probe)(struct wmi_device *wdev, const void *context);
int (*remove)(struct wmi_device *wdev);
void (*notify)(struct wmi_device *device, union acpi_object *data);
long (*filter_callback)(struct wmi_device *wdev, unsigned int cmd,
diff --git a/include/uapi/linux/isst_if.h b/include/uapi/linux/isst_if.h
new file mode 100644
index 000000000000..d10b832c58c5
--- /dev/null
+++ b/include/uapi/linux/isst_if.h
@@ -0,0 +1,172 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Intel Speed Select Interface: OS to hardware Interface
+ * Copyright (c) 2019, Intel Corporation.
+ * All rights reserved.
+ *
+ * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+ */
+
+#ifndef __ISST_IF_H
+#define __ISST_IF_H
+
+#include <linux/types.h>
+
+/**
+ * struct isst_if_platform_info - Define platform information
+ * @api_version: Version of the firmware document, which this driver
+ * can communicate
+ * @driver_version: Driver version, which will help user to send right
+ * commands. Even if the firmware is capable, driver may
+ * not be ready
+ * @max_cmds_per_ioctl: Returns the maximum number of commands driver will
+ * accept in a single ioctl
+ * @mbox_supported: Support of mail box interface
+ * @mmio_supported: Support of mmio interface for core-power feature
+ *
+ * Used to return output of IOCTL ISST_IF_GET_PLATFORM_INFO. This
+ * information can be used by the user space, to get the driver, firmware
+ * support and also number of commands to send in a single IOCTL request.
+ */
+struct isst_if_platform_info {
+ __u16 api_version;
+ __u16 driver_version;
+ __u16 max_cmds_per_ioctl;
+ __u8 mbox_supported;
+ __u8 mmio_supported;
+};
+
+/**
+ * struct isst_if_cpu_map - CPU mapping between logical and physical CPU
+ * @logical_cpu: Linux logical CPU number
+ * @physical_cpu: PUNIT CPU number
+ *
+ * Used to convert from Linux logical CPU to PUNIT CPU numbering scheme.
+ * The PUNIT CPU number is different than APIC ID based CPU numbering.
+ */
+struct isst_if_cpu_map {
+ __u32 logical_cpu;
+ __u32 physical_cpu;
+};
+
+/**
+ * struct isst_if_cpu_maps - structure for CPU map IOCTL
+ * @cmd_count: Number of CPU mapping command in cpu_map[]
+ * @cpu_map[]: Holds one or more CPU map data structure
+ *
+ * This structure used with ioctl ISST_IF_GET_PHY_ID to send
+ * one or more CPU mapping commands. Here IOCTL return value indicates
+ * number of commands sent or error number if no commands have been sent.
+ */
+struct isst_if_cpu_maps {
+ __u32 cmd_count;
+ struct isst_if_cpu_map cpu_map[1];
+};
+
+/**
+ * struct isst_if_io_reg - Read write PUNIT IO register
+ * @read_write: Value 0: Read, 1: Write
+ * @logical_cpu: Logical CPU number to get target PCI device.
+ * @reg: PUNIT register offset
+ * @value: For write operation value to write and for
+ * for read placeholder read value
+ *
+ * Structure to specify read/write data to PUNIT registers.
+ */
+struct isst_if_io_reg {
+ __u32 read_write; /* Read:0, Write:1 */
+ __u32 logical_cpu;
+ __u32 reg;
+ __u32 value;
+};
+
+/**
+ * struct isst_if_io_regs - structure for IO register commands
+ * @cmd_count: Number of io reg commands in io_reg[]
+ * @io_reg[]: Holds one or more io_reg command structure
+ *
+ * This structure used with ioctl ISST_IF_IO_CMD to send
+ * one or more read/write commands to PUNIT. Here IOCTL return value
+ * indicates number of requests sent or error number if no requests have
+ * been sent.
+ */
+struct isst_if_io_regs {
+ __u32 req_count;
+ struct isst_if_io_reg io_reg[1];
+};
+
+/**
+ * struct isst_if_mbox_cmd - Structure to define mail box command
+ * @logical_cpu: Logical CPU number to get target PCI device
+ * @parameter: Mailbox parameter value
+ * @req_data: Request data for the mailbox
+ * @resp_data: Response data for mailbox command response
+ * @command: Mailbox command value
+ * @sub_command: Mailbox sub command value
+ * @reserved: Unused, set to 0
+ *
+ * Structure to specify mailbox command to be sent to PUNIT.
+ */
+struct isst_if_mbox_cmd {
+ __u32 logical_cpu;
+ __u32 parameter;
+ __u32 req_data;
+ __u32 resp_data;
+ __u16 command;
+ __u16 sub_command;
+ __u32 reserved;
+};
+
+/**
+ * struct isst_if_mbox_cmds - structure for mailbox commands
+ * @cmd_count: Number of mailbox commands in mbox_cmd[]
+ * @mbox_cmd[]: Holds one or more mbox commands
+ *
+ * This structure used with ioctl ISST_IF_MBOX_COMMAND to send
+ * one or more mailbox commands to PUNIT. Here IOCTL return value
+ * indicates number of commands sent or error number if no commands have
+ * been sent.
+ */
+struct isst_if_mbox_cmds {
+ __u32 cmd_count;
+ struct isst_if_mbox_cmd mbox_cmd[1];
+};
+
+/**
+ * struct isst_if_msr_cmd - Structure to define msr command
+ * @read_write: Value 0: Read, 1: Write
+ * @logical_cpu: Logical CPU number
+ * @msr: MSR number
+ * @data: For write operation, data to write, for read
+ * place holder
+ *
+ * Structure to specify MSR command related to PUNIT.
+ */
+struct isst_if_msr_cmd {
+ __u32 read_write; /* Read:0, Write:1 */
+ __u32 logical_cpu;
+ __u64 msr;
+ __u64 data;
+};
+
+/**
+ * struct isst_if_msr_cmds - structure for msr commands
+ * @cmd_count: Number of mailbox commands in msr_cmd[]
+ * @msr_cmd[]: Holds one or more msr commands
+ *
+ * This structure used with ioctl ISST_IF_MSR_COMMAND to send
+ * one or more MSR commands. IOCTL return value indicates number of
+ * commands sent or error number if no commands have been sent.
+ */
+struct isst_if_msr_cmds {
+ __u32 cmd_count;
+ struct isst_if_msr_cmd msr_cmd[1];
+};
+
+#define ISST_IF_MAGIC 0xFE
+#define ISST_IF_GET_PLATFORM_INFO _IOR(ISST_IF_MAGIC, 0, struct isst_if_platform_info *)
+#define ISST_IF_GET_PHY_ID _IOWR(ISST_IF_MAGIC, 1, struct isst_if_cpu_map *)
+#define ISST_IF_IO_CMD _IOW(ISST_IF_MAGIC, 2, struct isst_if_io_regs *)
+#define ISST_IF_MBOX_COMMAND _IOWR(ISST_IF_MAGIC, 3, struct isst_if_mbox_cmds *)
+#define ISST_IF_MSR_COMMAND _IOWR(ISST_IF_MAGIC, 4, struct isst_if_msr_cmds *)
+#endif
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 785d708f8553..eea9d52b010c 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -2553,6 +2553,9 @@ unlock:
return ret;
}
+static bool exclusive_event_installable(struct perf_event *event,
+ struct perf_event_context *ctx);
+
/*
* Attach a performance event to a context.
*
@@ -2567,6 +2570,8 @@ perf_install_in_context(struct perf_event_context *ctx,
lockdep_assert_held(&ctx->mutex);
+ WARN_ON_ONCE(!exclusive_event_installable(event, ctx));
+
if (event->cpu != -1)
event->cpu = cpu;
@@ -4360,7 +4365,7 @@ static int exclusive_event_init(struct perf_event *event)
{
struct pmu *pmu = event->pmu;
- if (!(pmu->capabilities & PERF_PMU_CAP_EXCLUSIVE))
+ if (!is_exclusive_pmu(pmu))
return 0;
/*
@@ -4391,7 +4396,7 @@ static void exclusive_event_destroy(struct perf_event *event)
{
struct pmu *pmu = event->pmu;
- if (!(pmu->capabilities & PERF_PMU_CAP_EXCLUSIVE))
+ if (!is_exclusive_pmu(pmu))
return;
/* see comment in exclusive_event_init() */
@@ -4411,14 +4416,15 @@ static bool exclusive_event_match(struct perf_event *e1, struct perf_event *e2)
return false;
}
-/* Called under the same ctx::mutex as perf_install_in_context() */
static bool exclusive_event_installable(struct perf_event *event,
struct perf_event_context *ctx)
{
struct perf_event *iter_event;
struct pmu *pmu = event->pmu;
- if (!(pmu->capabilities & PERF_PMU_CAP_EXCLUSIVE))
+ lockdep_assert_held(&ctx->mutex);
+
+ if (!is_exclusive_pmu(pmu))
return true;
list_for_each_entry(iter_event, &ctx->event_list, event_entry) {
@@ -4465,12 +4471,20 @@ static void _free_event(struct perf_event *event)
if (event->destroy)
event->destroy(event);
- if (event->ctx)
- put_ctx(event->ctx);
-
+ /*
+ * Must be after ->destroy(), due to uprobe_perf_close() using
+ * hw.target.
+ */
if (event->hw.target)
put_task_struct(event->hw.target);
+ /*
+ * perf_event_free_task() relies on put_ctx() being 'last', in particular
+ * all task references must be cleaned up.
+ */
+ if (event->ctx)
+ put_ctx(event->ctx);
+
exclusive_event_destroy(event);
module_put(event->pmu->module);
@@ -4650,8 +4664,17 @@ again:
mutex_unlock(&event->child_mutex);
list_for_each_entry_safe(child, tmp, &free_list, child_list) {
+ void *var = &child->ctx->refcount;
+
list_del(&child->child_list);
free_event(child);
+
+ /*
+ * Wake any perf_event_free_task() waiting for this event to be
+ * freed.
+ */
+ smp_mb(); /* pairs with wait_var_event() */
+ wake_up_var(var);
}
no_ctx:
@@ -10930,11 +10953,6 @@ SYSCALL_DEFINE5(perf_event_open,
goto err_alloc;
}
- if ((pmu->capabilities & PERF_PMU_CAP_EXCLUSIVE) && group_leader) {
- err = -EBUSY;
- goto err_context;
- }
-
/*
* Look up the group leader (we will attach this event to it):
*/
@@ -11022,6 +11040,18 @@ SYSCALL_DEFINE5(perf_event_open,
move_group = 0;
}
}
+
+ /*
+ * Failure to create exclusive events returns -EBUSY.
+ */
+ err = -EBUSY;
+ if (!exclusive_event_installable(group_leader, ctx))
+ goto err_locked;
+
+ for_each_sibling_event(sibling, group_leader) {
+ if (!exclusive_event_installable(sibling, ctx))
+ goto err_locked;
+ }
} else {
mutex_lock(&ctx->mutex);
}
@@ -11058,9 +11088,6 @@ SYSCALL_DEFINE5(perf_event_open,
* because we need to serialize with concurrent event creation.
*/
if (!exclusive_event_installable(event, ctx)) {
- /* exclusive and group stuff are assumed mutually exclusive */
- WARN_ON_ONCE(move_group);
-
err = -EBUSY;
goto err_locked;
}
@@ -11527,11 +11554,11 @@ static void perf_free_event(struct perf_event *event,
}
/*
- * Free an unexposed, unused context as created by inheritance by
- * perf_event_init_task below, used by fork() in case of fail.
+ * Free a context as created by inheritance by perf_event_init_task() below,
+ * used by fork() in case of fail.
*
- * Not all locks are strictly required, but take them anyway to be nice and
- * help out with the lockdep assertions.
+ * Even though the task has never lived, the context and events have been
+ * exposed through the child_list, so we must take care tearing it all down.
*/
void perf_event_free_task(struct task_struct *task)
{
@@ -11561,7 +11588,23 @@ void perf_event_free_task(struct task_struct *task)
perf_free_event(event, ctx);
mutex_unlock(&ctx->mutex);
- put_ctx(ctx);
+
+ /*
+ * perf_event_release_kernel() could've stolen some of our
+ * child events and still have them on its free_list. In that
+ * case we must wait for these events to have been freed (in
+ * particular all their references to this task must've been
+ * dropped).
+ *
+ * Without this copy_process() will unconditionally free this
+ * task (irrespective of its reference count) and
+ * _free_event()'s put_task_struct(event->hw.target) will be a
+ * use-after-free.
+ *
+ * Wait for all events to drop their context reference.
+ */
+ wait_var_event(&ctx->refcount, refcount_read(&ctx->refcount) == 1);
+ put_ctx(ctx); /* must be last */
}
}
diff --git a/kernel/fork.c b/kernel/fork.c
index 8f3e2d97d771..dfa78985a6fd 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -677,7 +677,6 @@ void __mmdrop(struct mm_struct *mm)
WARN_ON_ONCE(mm == current->active_mm);
mm_free_pgd(mm);
destroy_context(mm);
- hmm_mm_destroy(mm);
mmu_notifier_mm_destroy(mm);
check_mm(mm);
put_user_ns(mm->user_ns);
diff --git a/kernel/locking/lockdep_proc.c b/kernel/locking/lockdep_proc.c
index 9c49ec645d8b..65b6a1600c8f 100644
--- a/kernel/locking/lockdep_proc.c
+++ b/kernel/locking/lockdep_proc.c
@@ -210,6 +210,7 @@ static int lockdep_stats_show(struct seq_file *m, void *v)
nr_hardirq_read_safe = 0, nr_hardirq_read_unsafe = 0,
sum_forward_deps = 0;
+#ifdef CONFIG_PROVE_LOCKING
list_for_each_entry(class, &all_lock_classes, lock_entry) {
if (class->usage_mask == 0)
@@ -241,13 +242,13 @@ static int lockdep_stats_show(struct seq_file *m, void *v)
if (class->usage_mask & LOCKF_ENABLED_HARDIRQ_READ)
nr_hardirq_read_unsafe++;
-#ifdef CONFIG_PROVE_LOCKING
sum_forward_deps += lockdep_count_forward_deps(class);
-#endif
}
#ifdef CONFIG_DEBUG_LOCKDEP
DEBUG_LOCKS_WARN_ON(debug_atomic_read(nr_unused_locks) != nr_unused);
#endif
+
+#endif
seq_printf(m, " lock-classes: %11lu [max: %lu]\n",
nr_lock_classes, MAX_LOCKDEP_KEYS);
seq_printf(m, " direct dependencies: %11lu [max: %lu]\n",
diff --git a/kernel/memremap.c b/kernel/memremap.c
index 6e1970719dc2..bea6f887adad 100644
--- a/kernel/memremap.c
+++ b/kernel/memremap.c
@@ -11,41 +11,39 @@
#include <linux/types.h>
#include <linux/wait_bit.h>
#include <linux/xarray.h>
-#include <linux/hmm.h>
static DEFINE_XARRAY(pgmap_array);
#define SECTION_MASK ~((1UL << PA_SECTION_SHIFT) - 1)
#define SECTION_SIZE (1UL << PA_SECTION_SHIFT)
-#if IS_ENABLED(CONFIG_DEVICE_PRIVATE)
-vm_fault_t device_private_entry_fault(struct vm_area_struct *vma,
- unsigned long addr,
- swp_entry_t entry,
- unsigned int flags,
- pmd_t *pmdp)
+#ifdef CONFIG_DEV_PAGEMAP_OPS
+DEFINE_STATIC_KEY_FALSE(devmap_managed_key);
+EXPORT_SYMBOL(devmap_managed_key);
+static atomic_t devmap_managed_enable;
+
+static void devmap_managed_enable_put(void *data)
{
- struct page *page = device_private_entry_to_page(entry);
- struct hmm_devmem *devmem;
+ if (atomic_dec_and_test(&devmap_managed_enable))
+ static_branch_disable(&devmap_managed_key);
+}
- devmem = container_of(page->pgmap, typeof(*devmem), pagemap);
+static int devmap_managed_enable_get(struct device *dev, struct dev_pagemap *pgmap)
+{
+ if (!pgmap->ops || !pgmap->ops->page_free) {
+ WARN(1, "Missing page_free method\n");
+ return -EINVAL;
+ }
- /*
- * The page_fault() callback must migrate page back to system memory
- * so that CPU can access it. This might fail for various reasons
- * (device issue, device was unsafely unplugged, ...). When such
- * error conditions happen, the callback must return VM_FAULT_SIGBUS.
- *
- * Note that because memory cgroup charges are accounted to the device
- * memory, this should never fail because of memory restrictions (but
- * allocation of regular system page might still fail because we are
- * out of memory).
- *
- * There is a more in-depth description of what that callback can and
- * cannot do, in include/linux/memremap.h
- */
- return devmem->page_fault(vma, addr, page, flags, pmdp);
+ if (atomic_inc_return(&devmap_managed_enable) == 1)
+ static_branch_enable(&devmap_managed_key);
+ return devm_add_action_or_reset(dev, devmap_managed_enable_put, NULL);
}
-#endif /* CONFIG_DEVICE_PRIVATE */
+#else
+static int devmap_managed_enable_get(struct device *dev, struct dev_pagemap *pgmap)
+{
+ return -EINVAL;
+}
+#endif /* CONFIG_DEV_PAGEMAP_OPS */
static void pgmap_array_delete(struct resource *res)
{
@@ -56,14 +54,8 @@ static void pgmap_array_delete(struct resource *res)
static unsigned long pfn_first(struct dev_pagemap *pgmap)
{
- const struct resource *res = &pgmap->res;
- struct vmem_altmap *altmap = &pgmap->altmap;
- unsigned long pfn;
-
- pfn = res->start >> PAGE_SHIFT;
- if (pgmap->altmap_valid)
- pfn += vmem_altmap_offset(altmap);
- return pfn;
+ return (pgmap->res.start >> PAGE_SHIFT) +
+ vmem_altmap_offset(pgmap_altmap(pgmap));
}
static unsigned long pfn_end(struct dev_pagemap *pgmap)
@@ -83,6 +75,24 @@ static unsigned long pfn_next(unsigned long pfn)
#define for_each_device_pfn(pfn, map) \
for (pfn = pfn_first(map); pfn < pfn_end(map); pfn = pfn_next(pfn))
+static void dev_pagemap_kill(struct dev_pagemap *pgmap)
+{
+ if (pgmap->ops && pgmap->ops->kill)
+ pgmap->ops->kill(pgmap);
+ else
+ percpu_ref_kill(pgmap->ref);
+}
+
+static void dev_pagemap_cleanup(struct dev_pagemap *pgmap)
+{
+ if (pgmap->ops && pgmap->ops->cleanup) {
+ pgmap->ops->cleanup(pgmap);
+ } else {
+ wait_for_completion(&pgmap->done);
+ percpu_ref_exit(pgmap->ref);
+ }
+}
+
static void devm_memremap_pages_release(void *data)
{
struct dev_pagemap *pgmap = data;
@@ -92,10 +102,10 @@ static void devm_memremap_pages_release(void *data)
unsigned long pfn;
int nid;
- pgmap->kill(pgmap->ref);
+ dev_pagemap_kill(pgmap);
for_each_device_pfn(pfn, pgmap)
put_page(pfn_to_page(pfn));
- pgmap->cleanup(pgmap->ref);
+ dev_pagemap_cleanup(pgmap);
/* pages are dead and unused, undo the arch mapping */
align_start = res->start & ~(SECTION_SIZE - 1);
@@ -111,7 +121,7 @@ static void devm_memremap_pages_release(void *data)
align_size >> PAGE_SHIFT, NULL);
} else {
arch_remove_memory(nid, align_start, align_size,
- pgmap->altmap_valid ? &pgmap->altmap : NULL);
+ pgmap_altmap(pgmap));
kasan_remove_zero_shadow(__va(align_start), align_size);
}
mem_hotplug_done();
@@ -122,20 +132,29 @@ static void devm_memremap_pages_release(void *data)
"%s: failed to free all reserved pages\n", __func__);
}
+static void dev_pagemap_percpu_release(struct percpu_ref *ref)
+{
+ struct dev_pagemap *pgmap =
+ container_of(ref, struct dev_pagemap, internal_ref);
+
+ complete(&pgmap->done);
+}
+
/**
* devm_memremap_pages - remap and provide memmap backing for the given resource
* @dev: hosting device for @res
* @pgmap: pointer to a struct dev_pagemap
*
* Notes:
- * 1/ At a minimum the res, ref and type members of @pgmap must be initialized
+ * 1/ At a minimum the res and type members of @pgmap must be initialized
* by the caller before passing it to this function
*
- * 2/ The altmap field may optionally be initialized, in which case altmap_valid
- * must be set to true
+ * 2/ The altmap field may optionally be initialized, in which case
+ * PGMAP_ALTMAP_VALID must be set in pgmap->flags.
*
- * 3/ pgmap->ref must be 'live' on entry and will be killed and reaped
- * at devm_memremap_pages_release() time, or if this routine fails.
+ * 3/ The ref field may optionally be provided, in which pgmap->ref must be
+ * 'live' on entry and will be killed and reaped at
+ * devm_memremap_pages_release() time, or if this routine fails.
*
* 4/ res is expected to be a host memory range that could feasibly be
* treated as a "System RAM" range, i.e. not a device mmio range, but
@@ -144,22 +163,66 @@ static void devm_memremap_pages_release(void *data)
void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap)
{
resource_size_t align_start, align_size, align_end;
- struct vmem_altmap *altmap = pgmap->altmap_valid ?
- &pgmap->altmap : NULL;
struct resource *res = &pgmap->res;
struct dev_pagemap *conflict_pgmap;
struct mhp_restrictions restrictions = {
/*
* We do not want any optional features only our own memmap
*/
- .altmap = altmap,
+ .altmap = pgmap_altmap(pgmap),
};
pgprot_t pgprot = PAGE_KERNEL;
int error, nid, is_ram;
+ bool need_devmap_managed = true;
+
+ switch (pgmap->type) {
+ case MEMORY_DEVICE_PRIVATE:
+ if (!IS_ENABLED(CONFIG_DEVICE_PRIVATE)) {
+ WARN(1, "Device private memory not supported\n");
+ return ERR_PTR(-EINVAL);
+ }
+ if (!pgmap->ops || !pgmap->ops->migrate_to_ram) {
+ WARN(1, "Missing migrate_to_ram method\n");
+ return ERR_PTR(-EINVAL);
+ }
+ break;
+ case MEMORY_DEVICE_FS_DAX:
+ if (!IS_ENABLED(CONFIG_ZONE_DEVICE) ||
+ IS_ENABLED(CONFIG_FS_DAX_LIMITED)) {
+ WARN(1, "File system DAX not supported\n");
+ return ERR_PTR(-EINVAL);
+ }
+ break;
+ case MEMORY_DEVICE_DEVDAX:
+ case MEMORY_DEVICE_PCI_P2PDMA:
+ need_devmap_managed = false;
+ break;
+ default:
+ WARN(1, "Invalid pgmap type %d\n", pgmap->type);
+ break;
+ }
+
+ if (!pgmap->ref) {
+ if (pgmap->ops && (pgmap->ops->kill || pgmap->ops->cleanup))
+ return ERR_PTR(-EINVAL);
+
+ init_completion(&pgmap->done);
+ error = percpu_ref_init(&pgmap->internal_ref,
+ dev_pagemap_percpu_release, 0, GFP_KERNEL);
+ if (error)
+ return ERR_PTR(error);
+ pgmap->ref = &pgmap->internal_ref;
+ } else {
+ if (!pgmap->ops || !pgmap->ops->kill || !pgmap->ops->cleanup) {
+ WARN(1, "Missing reference count teardown definition\n");
+ return ERR_PTR(-EINVAL);
+ }
+ }
- if (!pgmap->ref || !pgmap->kill || !pgmap->cleanup) {
- WARN(1, "Missing reference count teardown definition\n");
- return ERR_PTR(-EINVAL);
+ if (need_devmap_managed) {
+ error = devmap_managed_enable_get(dev, pgmap);
+ if (error)
+ return ERR_PTR(error);
}
align_start = res->start & ~(SECTION_SIZE - 1);
@@ -241,7 +304,7 @@ void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap)
zone = &NODE_DATA(nid)->node_zones[ZONE_DEVICE];
move_pfn_range_to_zone(zone, align_start >> PAGE_SHIFT,
- align_size >> PAGE_SHIFT, altmap);
+ align_size >> PAGE_SHIFT, pgmap_altmap(pgmap));
}
mem_hotplug_done();
@@ -271,9 +334,8 @@ void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap)
err_pfn_remap:
pgmap_array_delete(res);
err_array:
- pgmap->kill(pgmap->ref);
- pgmap->cleanup(pgmap->ref);
-
+ dev_pagemap_kill(pgmap);
+ dev_pagemap_cleanup(pgmap);
return ERR_PTR(error);
}
EXPORT_SYMBOL_GPL(devm_memremap_pages);
@@ -287,7 +349,9 @@ EXPORT_SYMBOL_GPL(devm_memunmap_pages);
unsigned long vmem_altmap_offset(struct vmem_altmap *altmap)
{
/* number of pfns from base where pfn_to_page() is valid */
- return altmap->reserve + altmap->free;
+ if (altmap)
+ return altmap->reserve + altmap->free;
+ return 0;
}
void vmem_altmap_free(struct vmem_altmap *altmap, unsigned long nr_pfns)
@@ -329,28 +393,6 @@ struct dev_pagemap *get_dev_pagemap(unsigned long pfn,
EXPORT_SYMBOL_GPL(get_dev_pagemap);
#ifdef CONFIG_DEV_PAGEMAP_OPS
-DEFINE_STATIC_KEY_FALSE(devmap_managed_key);
-EXPORT_SYMBOL(devmap_managed_key);
-static atomic_t devmap_enable;
-
-/*
- * Toggle the static key for ->page_free() callbacks when dev_pagemap
- * pages go idle.
- */
-void dev_pagemap_get_ops(void)
-{
- if (atomic_inc_return(&devmap_enable) == 1)
- static_branch_enable(&devmap_managed_key);
-}
-EXPORT_SYMBOL_GPL(dev_pagemap_get_ops);
-
-void dev_pagemap_put_ops(void)
-{
- if (atomic_dec_and_test(&devmap_enable))
- static_branch_disable(&devmap_managed_key);
-}
-EXPORT_SYMBOL_GPL(dev_pagemap_put_ops);
-
void __put_devmap_managed_page(struct page *page)
{
int count = page_ref_dec_return(page);
@@ -366,7 +408,7 @@ void __put_devmap_managed_page(struct page *page)
mem_cgroup_uncharge(page);
- page->pgmap->page_free(page, page->pgmap->data);
+ page->pgmap->ops->page_free(page);
} else if (!count)
__put_page(page);
}
diff --git a/kernel/resource.c b/kernel/resource.c
index 158f04ec1d4f..d22423e85cf8 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -1628,6 +1628,45 @@ void resource_list_free(struct list_head *head)
}
EXPORT_SYMBOL(resource_list_free);
+#ifdef CONFIG_DEVICE_PRIVATE
+/**
+ * devm_request_free_mem_region - find free region for device private memory
+ *
+ * @dev: device struct to bind the resource to
+ * @size: size in bytes of the device memory to add
+ * @base: resource tree to look in
+ *
+ * This function tries to find an empty range of physical address big enough to
+ * contain the new resource, so that it can later be hotplugged as ZONE_DEVICE
+ * memory, which in turn allocates struct pages.
+ */
+struct resource *devm_request_free_mem_region(struct device *dev,
+ struct resource *base, unsigned long size)
+{
+ resource_size_t end, addr;
+ struct resource *res;
+
+ size = ALIGN(size, 1UL << PA_SECTION_SHIFT);
+ end = min_t(unsigned long, base->end, (1UL << MAX_PHYSMEM_BITS) - 1);
+ addr = end - size + 1UL;
+
+ for (; addr > size && addr >= base->start; addr -= size) {
+ if (region_intersects(addr, size, 0, IORES_DESC_NONE) !=
+ REGION_DISJOINT)
+ continue;
+
+ res = devm_request_mem_region(dev, addr, size, dev_name(dev));
+ if (!res)
+ return ERR_PTR(-ENOMEM);
+ res->desc = IORES_DESC_DEVICE_PRIVATE_MEMORY;
+ return res;
+ }
+
+ return ERR_PTR(-ERANGE);
+}
+EXPORT_SYMBOL_GPL(devm_request_free_mem_region);
+#endif /* CONFIG_DEVICE_PRIVATE */
+
static int __init strict_iomem(char *str)
{
if (strstr(str, "relaxed"))
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index fa43ce3962e7..2b037f195473 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -2399,6 +2399,7 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
unsigned long flags;
int cpu, success = 0;
+ preempt_disable();
if (p == current) {
/*
* We're waking current, this means 'p->on_rq' and 'task_cpu(p)
@@ -2412,7 +2413,7 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
* it disabling IRQs (this allows not taking ->pi_lock).
*/
if (!(p->state & state))
- return false;
+ goto out;
success = 1;
cpu = task_cpu(p);
@@ -2526,6 +2527,7 @@ unlock:
out:
if (success)
ttwu_stat(p, cpu, wake_flags);
+ preempt_enable();
return success;
}
diff --git a/lib/percpu-refcount.c b/lib/percpu-refcount.c
index 071a76c7bac0..4f6c6ebbbbde 100644
--- a/lib/percpu-refcount.c
+++ b/lib/percpu-refcount.c
@@ -70,11 +70,14 @@ int percpu_ref_init(struct percpu_ref *ref, percpu_ref_func_t *release,
return -ENOMEM;
ref->force_atomic = flags & PERCPU_REF_INIT_ATOMIC;
+ ref->allow_reinit = flags & PERCPU_REF_ALLOW_REINIT;
- if (flags & (PERCPU_REF_INIT_ATOMIC | PERCPU_REF_INIT_DEAD))
+ if (flags & (PERCPU_REF_INIT_ATOMIC | PERCPU_REF_INIT_DEAD)) {
ref->percpu_count_ptr |= __PERCPU_REF_ATOMIC;
- else
+ ref->allow_reinit = true;
+ } else {
start_count += PERCPU_COUNT_BIAS;
+ }
if (flags & PERCPU_REF_INIT_DEAD)
ref->percpu_count_ptr |= __PERCPU_REF_DEAD;
@@ -120,6 +123,9 @@ static void percpu_ref_call_confirm_rcu(struct rcu_head *rcu)
ref->confirm_switch = NULL;
wake_up_all(&percpu_ref_switch_waitq);
+ if (!ref->allow_reinit)
+ percpu_ref_exit(ref);
+
/* drop ref from percpu_ref_switch_to_atomic() */
percpu_ref_put(ref);
}
@@ -195,6 +201,9 @@ static void __percpu_ref_switch_to_percpu(struct percpu_ref *ref)
if (!(ref->percpu_count_ptr & __PERCPU_REF_ATOMIC))
return;
+ if (WARN_ON_ONCE(!ref->allow_reinit))
+ return;
+
atomic_long_add(PERCPU_COUNT_BIAS, &ref->count);
/*
diff --git a/mm/Kconfig b/mm/Kconfig
index 0b4352557dd5..495d7368ced8 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -670,47 +670,17 @@ config ZONE_DEVICE
If FS_DAX is enabled, then say Y.
-config ARCH_HAS_HMM_MIRROR
- bool
- default y
- depends on (X86_64 || PPC64)
- depends on MMU && 64BIT
-
-config ARCH_HAS_HMM_DEVICE
- bool
- default y
- depends on (X86_64 || PPC64)
- depends on MEMORY_HOTPLUG
- depends on MEMORY_HOTREMOVE
- depends on SPARSEMEM_VMEMMAP
- depends on ARCH_HAS_ZONE_DEVICE
- select XARRAY_MULTI
-
-config ARCH_HAS_HMM
- bool
- default y
- depends on (X86_64 || PPC64)
- depends on ZONE_DEVICE
- depends on MMU && 64BIT
- depends on MEMORY_HOTPLUG
- depends on MEMORY_HOTREMOVE
- depends on SPARSEMEM_VMEMMAP
-
config MIGRATE_VMA_HELPER
bool
config DEV_PAGEMAP_OPS
bool
-config HMM
- bool
- select MMU_NOTIFIER
- select MIGRATE_VMA_HELPER
-
config HMM_MIRROR
bool "HMM mirror CPU page table into a device page table"
- depends on ARCH_HAS_HMM
- select HMM
+ depends on (X86_64 || PPC64)
+ depends on MMU && 64BIT
+ select MMU_NOTIFIER
help
Select HMM_MIRROR if you want to mirror range of the CPU page table of a
process into a device page table. Here, mirror means "keep synchronized".
@@ -720,8 +690,7 @@ config HMM_MIRROR
config DEVICE_PRIVATE
bool "Unaddressable device memory (GPU memory, ...)"
- depends on ARCH_HAS_HMM
- select HMM
+ depends on ZONE_DEVICE
select DEV_PAGEMAP_OPS
help
@@ -729,17 +698,6 @@ config DEVICE_PRIVATE
memory; i.e., memory that is only accessible from the device (or
group of devices). You likely also want to select HMM_MIRROR.
-config DEVICE_PUBLIC
- bool "Addressable device memory (like GPU memory)"
- depends on ARCH_HAS_HMM
- select HMM
- select DEV_PAGEMAP_OPS
-
- help
- Allows creation of struct pages to represent addressable device
- memory; i.e., memory that is accessible from both the device and
- the CPU
-
config FRAME_VECTOR
bool
diff --git a/mm/Makefile b/mm/Makefile
index dc0746ca1109..338e528ad436 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -102,5 +102,5 @@ obj-$(CONFIG_FRAME_VECTOR) += frame_vector.o
obj-$(CONFIG_DEBUG_PAGE_REF) += debug_page_ref.o
obj-$(CONFIG_HARDENED_USERCOPY) += usercopy.o
obj-$(CONFIG_PERCPU_STATS) += percpu-stats.o
-obj-$(CONFIG_HMM) += hmm.o
+obj-$(CONFIG_HMM_MIRROR) += hmm.o
obj-$(CONFIG_MEMFD_CREATE) += memfd.o
diff --git a/mm/gup.c b/mm/gup.c
index 43b7d875de37..8bbaa5523116 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -609,13 +609,6 @@ static int get_gate_page(struct mm_struct *mm, unsigned long address,
if ((gup_flags & FOLL_DUMP) || !is_zero_pfn(pte_pfn(*pte)))
goto unmap;
*page = pte_page(*pte);
-
- /*
- * This should never happen (a device public page in the gate
- * area).
- */
- if (is_device_public_page(*page))
- goto unmap;
}
if (unlikely(!try_get_page(*page))) {
ret = -ENOMEM;
diff --git a/mm/hmm.c b/mm/hmm.c
index f702a3895d05..e1eedef129cf 100644
--- a/mm/hmm.c
+++ b/mm/hmm.c
@@ -20,26 +20,14 @@
#include <linux/swapops.h>
#include <linux/hugetlb.h>
#include <linux/memremap.h>
+#include <linux/sched/mm.h>
#include <linux/jump_label.h>
#include <linux/dma-mapping.h>
#include <linux/mmu_notifier.h>
#include <linux/memory_hotplug.h>
-#define PA_SECTION_SIZE (1UL << PA_SECTION_SHIFT)
-
-#if IS_ENABLED(CONFIG_HMM_MIRROR)
static const struct mmu_notifier_ops hmm_mmu_notifier_ops;
-static inline struct hmm *mm_get_hmm(struct mm_struct *mm)
-{
- struct hmm *hmm = READ_ONCE(mm->hmm);
-
- if (hmm && kref_get_unless_zero(&hmm->kref))
- return hmm;
-
- return NULL;
-}
-
/**
* hmm_get_or_create - register HMM against an mm (HMM internal)
*
@@ -54,11 +42,16 @@ static inline struct hmm *mm_get_hmm(struct mm_struct *mm)
*/
static struct hmm *hmm_get_or_create(struct mm_struct *mm)
{
- struct hmm *hmm = mm_get_hmm(mm);
- bool cleanup = false;
+ struct hmm *hmm;
- if (hmm)
- return hmm;
+ lockdep_assert_held_write(&mm->mmap_sem);
+
+ /* Abuse the page_table_lock to also protect mm->hmm. */
+ spin_lock(&mm->page_table_lock);
+ hmm = mm->hmm;
+ if (mm->hmm && kref_get_unless_zero(&mm->hmm->kref))
+ goto out_unlock;
+ spin_unlock(&mm->page_table_lock);
hmm = kmalloc(sizeof(*hmm), GFP_KERNEL);
if (!hmm)
@@ -68,55 +61,50 @@ static struct hmm *hmm_get_or_create(struct mm_struct *mm)
init_rwsem(&hmm->mirrors_sem);
hmm->mmu_notifier.ops = NULL;
INIT_LIST_HEAD(&hmm->ranges);
- mutex_init(&hmm->lock);
+ spin_lock_init(&hmm->ranges_lock);
kref_init(&hmm->kref);
hmm->notifiers = 0;
- hmm->dead = false;
hmm->mm = mm;
- spin_lock(&mm->page_table_lock);
- if (!mm->hmm)
- mm->hmm = hmm;
- else
- cleanup = true;
- spin_unlock(&mm->page_table_lock);
+ hmm->mmu_notifier.ops = &hmm_mmu_notifier_ops;
+ if (__mmu_notifier_register(&hmm->mmu_notifier, mm)) {
+ kfree(hmm);
+ return NULL;
+ }
- if (cleanup)
- goto error;
+ mmgrab(hmm->mm);
/*
- * We should only get here if hold the mmap_sem in write mode ie on
- * registration of first mirror through hmm_mirror_register()
+ * We hold the exclusive mmap_sem here so we know that mm->hmm is
+ * still NULL or 0 kref, and is safe to update.
*/
- hmm->mmu_notifier.ops = &hmm_mmu_notifier_ops;
- if (__mmu_notifier_register(&hmm->mmu_notifier, mm))
- goto error_mm;
+ spin_lock(&mm->page_table_lock);
+ mm->hmm = hmm;
+out_unlock:
+ spin_unlock(&mm->page_table_lock);
return hmm;
+}
-error_mm:
- spin_lock(&mm->page_table_lock);
- if (mm->hmm == hmm)
- mm->hmm = NULL;
- spin_unlock(&mm->page_table_lock);
-error:
+static void hmm_free_rcu(struct rcu_head *rcu)
+{
+ struct hmm *hmm = container_of(rcu, struct hmm, rcu);
+
+ mmdrop(hmm->mm);
kfree(hmm);
- return NULL;
}
static void hmm_free(struct kref *kref)
{
struct hmm *hmm = container_of(kref, struct hmm, kref);
- struct mm_struct *mm = hmm->mm;
- mmu_notifier_unregister_no_release(&hmm->mmu_notifier, mm);
+ spin_lock(&hmm->mm->page_table_lock);
+ if (hmm->mm->hmm == hmm)
+ hmm->mm->hmm = NULL;
+ spin_unlock(&hmm->mm->page_table_lock);
- spin_lock(&mm->page_table_lock);
- if (mm->hmm == hmm)
- mm->hmm = NULL;
- spin_unlock(&mm->page_table_lock);
-
- kfree(hmm);
+ mmu_notifier_unregister_no_release(&hmm->mmu_notifier, hmm->mm);
+ mmu_notifier_call_srcu(&hmm->rcu, hmm_free_rcu);
}
static inline void hmm_put(struct hmm *hmm)
@@ -124,86 +112,73 @@ static inline void hmm_put(struct hmm *hmm)
kref_put(&hmm->kref, hmm_free);
}
-void hmm_mm_destroy(struct mm_struct *mm)
+static void hmm_release(struct mmu_notifier *mn, struct mm_struct *mm)
{
- struct hmm *hmm;
+ struct hmm *hmm = container_of(mn, struct hmm, mmu_notifier);
+ struct hmm_mirror *mirror;
- spin_lock(&mm->page_table_lock);
- hmm = mm_get_hmm(mm);
- mm->hmm = NULL;
- if (hmm) {
- hmm->mm = NULL;
- hmm->dead = true;
- spin_unlock(&mm->page_table_lock);
- hmm_put(hmm);
+ /* Bail out if hmm is in the process of being freed */
+ if (!kref_get_unless_zero(&hmm->kref))
return;
+
+ /*
+ * Since hmm_range_register() holds the mmget() lock hmm_release() is
+ * prevented as long as a range exists.
+ */
+ WARN_ON(!list_empty_careful(&hmm->ranges));
+
+ down_read(&hmm->mirrors_sem);
+ list_for_each_entry(mirror, &hmm->mirrors, list) {
+ /*
+ * Note: The driver is not allowed to trigger
+ * hmm_mirror_unregister() from this thread.
+ */
+ if (mirror->ops->release)
+ mirror->ops->release(mirror);
}
+ up_read(&hmm->mirrors_sem);
- spin_unlock(&mm->page_table_lock);
+ hmm_put(hmm);
}
-static void hmm_release(struct mmu_notifier *mn, struct mm_struct *mm)
+static void notifiers_decrement(struct hmm *hmm)
{
- struct hmm *hmm = mm_get_hmm(mm);
- struct hmm_mirror *mirror;
- struct hmm_range *range;
-
- /* Report this HMM as dying. */
- hmm->dead = true;
+ unsigned long flags;
- /* Wake-up everyone waiting on any range. */
- mutex_lock(&hmm->lock);
- list_for_each_entry(range, &hmm->ranges, list) {
- range->valid = false;
- }
- wake_up_all(&hmm->wq);
- mutex_unlock(&hmm->lock);
+ spin_lock_irqsave(&hmm->ranges_lock, flags);
+ hmm->notifiers--;
+ if (!hmm->notifiers) {
+ struct hmm_range *range;
- down_write(&hmm->mirrors_sem);
- mirror = list_first_entry_or_null(&hmm->mirrors, struct hmm_mirror,
- list);
- while (mirror) {
- list_del_init(&mirror->list);
- if (mirror->ops->release) {
- /*
- * Drop mirrors_sem so callback can wait on any pending
- * work that might itself trigger mmu_notifier callback
- * and thus would deadlock with us.
- */
- up_write(&hmm->mirrors_sem);
- mirror->ops->release(mirror);
- down_write(&hmm->mirrors_sem);
+ list_for_each_entry(range, &hmm->ranges, list) {
+ if (range->valid)
+ continue;
+ range->valid = true;
}
- mirror = list_first_entry_or_null(&hmm->mirrors,
- struct hmm_mirror, list);
+ wake_up_all(&hmm->wq);
}
- up_write(&hmm->mirrors_sem);
-
- hmm_put(hmm);
+ spin_unlock_irqrestore(&hmm->ranges_lock, flags);
}
static int hmm_invalidate_range_start(struct mmu_notifier *mn,
const struct mmu_notifier_range *nrange)
{
- struct hmm *hmm = mm_get_hmm(nrange->mm);
+ struct hmm *hmm = container_of(mn, struct hmm, mmu_notifier);
struct hmm_mirror *mirror;
struct hmm_update update;
struct hmm_range *range;
+ unsigned long flags;
int ret = 0;
- VM_BUG_ON(!hmm);
+ if (!kref_get_unless_zero(&hmm->kref))
+ return 0;
update.start = nrange->start;
update.end = nrange->end;
update.event = HMM_UPDATE_INVALIDATE;
update.blockable = mmu_notifier_range_blockable(nrange);
- if (mmu_notifier_range_blockable(nrange))
- mutex_lock(&hmm->lock);
- else if (!mutex_trylock(&hmm->lock)) {
- ret = -EAGAIN;
- goto out;
- }
+ spin_lock_irqsave(&hmm->ranges_lock, flags);
hmm->notifiers++;
list_for_each_entry(range, &hmm->ranges, list) {
if (update.end < range->start || update.start >= range->end)
@@ -211,7 +186,7 @@ static int hmm_invalidate_range_start(struct mmu_notifier *mn,
range->valid = false;
}
- mutex_unlock(&hmm->lock);
+ spin_unlock_irqrestore(&hmm->ranges_lock, flags);
if (mmu_notifier_range_blockable(nrange))
down_read(&hmm->mirrors_sem);
@@ -219,19 +194,23 @@ static int hmm_invalidate_range_start(struct mmu_notifier *mn,
ret = -EAGAIN;
goto out;
}
+
list_for_each_entry(mirror, &hmm->mirrors, list) {
- int ret;
+ int rc;
- ret = mirror->ops->sync_cpu_device_pagetables(mirror, &update);
- if (!update.blockable && ret == -EAGAIN) {
- up_read(&hmm->mirrors_sem);
+ rc = mirror->ops->sync_cpu_device_pagetables(mirror, &update);
+ if (rc) {
+ if (WARN_ON(update.blockable || rc != -EAGAIN))
+ continue;
ret = -EAGAIN;
- goto out;
+ break;
}
}
up_read(&hmm->mirrors_sem);
out:
+ if (ret)
+ notifiers_decrement(hmm);
hmm_put(hmm);
return ret;
}
@@ -239,24 +218,12 @@ out:
static void hmm_invalidate_range_end(struct mmu_notifier *mn,
const struct mmu_notifier_range *nrange)
{
- struct hmm *hmm = mm_get_hmm(nrange->mm);
-
- VM_BUG_ON(!hmm);
+ struct hmm *hmm = container_of(mn, struct hmm, mmu_notifier);
- mutex_lock(&hmm->lock);
- hmm->notifiers--;
- if (!hmm->notifiers) {
- struct hmm_range *range;
-
- list_for_each_entry(range, &hmm->ranges, list) {
- if (range->valid)
- continue;
- range->valid = true;
- }
- wake_up_all(&hmm->wq);
- }
- mutex_unlock(&hmm->lock);
+ if (!kref_get_unless_zero(&hmm->kref))
+ return;
+ notifiers_decrement(hmm);
hmm_put(hmm);
}
@@ -271,14 +238,15 @@ static const struct mmu_notifier_ops hmm_mmu_notifier_ops = {
*
* @mirror: new mirror struct to register
* @mm: mm to register against
+ * Return: 0 on success, -ENOMEM if no memory, -EINVAL if invalid arguments
*
* To start mirroring a process address space, the device driver must register
* an HMM mirror struct.
- *
- * THE mm->mmap_sem MUST BE HELD IN WRITE MODE !
*/
int hmm_mirror_register(struct hmm_mirror *mirror, struct mm_struct *mm)
{
+ lockdep_assert_held_write(&mm->mmap_sem);
+
/* Sanity check */
if (!mm || !mirror || !mirror->ops)
return -EINVAL;
@@ -298,23 +266,17 @@ EXPORT_SYMBOL(hmm_mirror_register);
/*
* hmm_mirror_unregister() - unregister a mirror
*
- * @mirror: new mirror struct to register
+ * @mirror: mirror struct to unregister
*
* Stop mirroring a process address space, and cleanup.
*/
void hmm_mirror_unregister(struct hmm_mirror *mirror)
{
- struct hmm *hmm = READ_ONCE(mirror->hmm);
-
- if (hmm == NULL)
- return;
+ struct hmm *hmm = mirror->hmm;
down_write(&hmm->mirrors_sem);
- list_del_init(&mirror->list);
- /* To protect us against double unregister ... */
- mirror->hmm = NULL;
+ list_del(&mirror->list);
up_write(&hmm->mirrors_sem);
-
hmm_put(hmm);
}
EXPORT_SYMBOL(hmm_mirror_unregister);
@@ -330,7 +292,7 @@ struct hmm_vma_walk {
static int hmm_vma_do_fault(struct mm_walk *walk, unsigned long addr,
bool write_fault, uint64_t *pfn)
{
- unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_REMOTE;
+ unsigned int flags = FAULT_FLAG_REMOTE;
struct hmm_vma_walk *hmm_vma_walk = walk->private;
struct hmm_range *range = hmm_vma_walk->range;
struct vm_area_struct *vma = walk->vma;
@@ -372,7 +334,7 @@ static int hmm_pfns_bad(unsigned long addr,
* @fault: should we fault or not ?
* @write_fault: write fault ?
* @walk: mm_walk structure
- * Returns: 0 on success, -EBUSY after page fault, or page fault error
+ * Return: 0 on success, -EBUSY after page fault, or page fault error
*
* This function will be called whenever pmd_none() or pte_none() returns true,
* or whenever there is no page directory covering the virtual address range.
@@ -550,7 +512,7 @@ static int hmm_vma_handle_pmd(struct mm_walk *walk,
static inline uint64_t pte_to_hmm_pfn_flags(struct hmm_range *range, pte_t pte)
{
- if (pte_none(pte) || !pte_present(pte))
+ if (pte_none(pte) || !pte_present(pte) || pte_protnone(pte))
return 0;
return pte_write(pte) ? range->flags[HMM_PFN_VALID] |
range->flags[HMM_PFN_WRITE] :
@@ -788,7 +750,6 @@ again:
return hmm_vma_walk_hole_(addr, end, fault,
write_fault, walk);
-#ifdef CONFIG_HUGETLB_PAGE
pfn = pud_pfn(pud) + ((addr & ~PUD_MASK) >> PAGE_SHIFT);
for (i = 0; i < npages; ++i, ++pfn) {
hmm_vma_walk->pgmap = get_dev_pagemap(pfn,
@@ -804,9 +765,6 @@ again:
}
hmm_vma_walk->last = end;
return 0;
-#else
- return -EINVAL;
-#endif
}
split_huge_pud(walk->vma, pudp, addr);
@@ -909,12 +867,14 @@ static void hmm_pfns_clear(struct hmm_range *range,
* Track updates to the CPU page table see include/linux/hmm.h
*/
int hmm_range_register(struct hmm_range *range,
- struct mm_struct *mm,
+ struct hmm_mirror *mirror,
unsigned long start,
unsigned long end,
unsigned page_shift)
{
unsigned long mask = ((1UL << page_shift) - 1UL);
+ struct hmm *hmm = mirror->hmm;
+ unsigned long flags;
range->valid = false;
range->hmm = NULL;
@@ -928,28 +888,24 @@ int hmm_range_register(struct hmm_range *range,
range->start = start;
range->end = end;
- range->hmm = hmm_get_or_create(mm);
- if (!range->hmm)
- return -EFAULT;
-
- /* Check if hmm_mm_destroy() was call. */
- if (range->hmm->mm == NULL || range->hmm->dead) {
- hmm_put(range->hmm);
+ /* Prevent hmm_release() from running while the range is valid */
+ if (!mmget_not_zero(hmm->mm))
return -EFAULT;
- }
- /* Initialize range to track CPU page table update */
- mutex_lock(&range->hmm->lock);
+ /* Initialize range to track CPU page table updates. */
+ spin_lock_irqsave(&hmm->ranges_lock, flags);
- list_add_rcu(&range->list, &range->hmm->ranges);
+ range->hmm = hmm;
+ kref_get(&hmm->kref);
+ list_add(&range->list, &hmm->ranges);
/*
* If there are any concurrent notifiers we have to wait for them for
* the range to be valid (see hmm_range_wait_until_valid()).
*/
- if (!range->hmm->notifiers)
+ if (!hmm->notifiers)
range->valid = true;
- mutex_unlock(&range->hmm->lock);
+ spin_unlock_irqrestore(&hmm->ranges_lock, flags);
return 0;
}
@@ -964,25 +920,31 @@ EXPORT_SYMBOL(hmm_range_register);
*/
void hmm_range_unregister(struct hmm_range *range)
{
- /* Sanity check this really should not happen. */
- if (range->hmm == NULL || range->end <= range->start)
- return;
+ struct hmm *hmm = range->hmm;
+ unsigned long flags;
- mutex_lock(&range->hmm->lock);
- list_del_rcu(&range->list);
- mutex_unlock(&range->hmm->lock);
+ spin_lock_irqsave(&hmm->ranges_lock, flags);
+ list_del_init(&range->list);
+ spin_unlock_irqrestore(&hmm->ranges_lock, flags);
/* Drop reference taken by hmm_range_register() */
+ mmput(hmm->mm);
+ hmm_put(hmm);
+
+ /*
+ * The range is now invalid and the ref on the hmm is dropped, so
+ * poison the pointer. Leave other fields in place, for the caller's
+ * use.
+ */
range->valid = false;
- hmm_put(range->hmm);
- range->hmm = NULL;
+ memset(&range->hmm, POISON_INUSE, sizeof(range->hmm));
}
EXPORT_SYMBOL(hmm_range_unregister);
/*
* hmm_range_snapshot() - snapshot CPU page table for a range
* @range: range
- * Returns: -EINVAL if invalid argument, -ENOMEM out of memory, -EPERM invalid
+ * Return: -EINVAL if invalid argument, -ENOMEM out of memory, -EPERM invalid
* permission (for instance asking for write and range is read only),
* -EAGAIN if you need to retry, -EFAULT invalid (ie either no valid
* vma or it is illegal to access that range), number of valid pages
@@ -1001,10 +963,7 @@ long hmm_range_snapshot(struct hmm_range *range)
struct vm_area_struct *vma;
struct mm_walk mm_walk;
- /* Check if hmm_mm_destroy() was call. */
- if (hmm->mm == NULL || hmm->dead)
- return -EFAULT;
-
+ lockdep_assert_held(&hmm->mm->mmap_sem);
do {
/* If range is no longer valid force retry. */
if (!range->valid)
@@ -1015,9 +974,8 @@ long hmm_range_snapshot(struct hmm_range *range)
return -EFAULT;
if (is_vm_hugetlb_page(vma)) {
- struct hstate *h = hstate_vma(vma);
-
- if (huge_page_shift(h) != range->page_shift &&
+ if (huge_page_shift(hstate_vma(vma)) !=
+ range->page_shift &&
range->page_shift != PAGE_SHIFT)
return -EINVAL;
} else {
@@ -1066,7 +1024,7 @@ EXPORT_SYMBOL(hmm_range_snapshot);
* hmm_range_fault() - try to fault some address in a virtual address range
* @range: range being faulted
* @block: allow blocking on fault (if true it sleeps and do not drop mmap_sem)
- * Returns: number of valid pages in range->pfns[] (from range start
+ * Return: number of valid pages in range->pfns[] (from range start
* address). This may be zero. If the return value is negative,
* then one of the following values may be returned:
*
@@ -1100,9 +1058,7 @@ long hmm_range_fault(struct hmm_range *range, bool block)
struct mm_walk mm_walk;
int ret;
- /* Check if hmm_mm_destroy() was call. */
- if (hmm->mm == NULL || hmm->dead)
- return -EFAULT;
+ lockdep_assert_held(&hmm->mm->mmap_sem);
do {
/* If range is no longer valid force retry. */
@@ -1184,7 +1140,7 @@ EXPORT_SYMBOL(hmm_range_fault);
* @device: device against to dma map page to
* @daddrs: dma address of mapped pages
* @block: allow blocking on fault (if true it sleeps and do not drop mmap_sem)
- * Returns: number of pages mapped on success, -EAGAIN if mmap_sem have been
+ * Return: number of pages mapped on success, -EAGAIN if mmap_sem have been
* drop and you need to try again, some other error value otherwise
*
* Note same usage pattern as hmm_range_fault().
@@ -1272,7 +1228,7 @@ EXPORT_SYMBOL(hmm_range_dma_map);
* @device: device against which dma map was done
* @daddrs: dma address of mapped pages
* @dirty: dirty page if it had the write flag set
- * Returns: number of page unmapped on success, -EINVAL otherwise
+ * Return: number of page unmapped on success, -EINVAL otherwise
*
* Note that caller MUST abide by mmu notifier or use HMM mirror and abide
* to the sync_cpu_device_pagetables() callback so that it is safe here to
@@ -1328,284 +1284,3 @@ long hmm_range_dma_unmap(struct hmm_range *range,
return cpages;
}
EXPORT_SYMBOL(hmm_range_dma_unmap);
-#endif /* IS_ENABLED(CONFIG_HMM_MIRROR) */
-
-
-#if IS_ENABLED(CONFIG_DEVICE_PRIVATE) || IS_ENABLED(CONFIG_DEVICE_PUBLIC)
-struct page *hmm_vma_alloc_locked_page(struct vm_area_struct *vma,
- unsigned long addr)
-{
- struct page *page;
-
- page = alloc_page_vma(GFP_HIGHUSER, vma, addr);
- if (!page)
- return NULL;
- lock_page(page);
- return page;
-}
-EXPORT_SYMBOL(hmm_vma_alloc_locked_page);
-
-
-static void hmm_devmem_ref_release(struct percpu_ref *ref)
-{
- struct hmm_devmem *devmem;
-
- devmem = container_of(ref, struct hmm_devmem, ref);
- complete(&devmem->completion);
-}
-
-static void hmm_devmem_ref_exit(struct percpu_ref *ref)
-{
- struct hmm_devmem *devmem;
-
- devmem = container_of(ref, struct hmm_devmem, ref);
- wait_for_completion(&devmem->completion);
- percpu_ref_exit(ref);
-}
-
-static void hmm_devmem_ref_kill(struct percpu_ref *ref)
-{
- percpu_ref_kill(ref);
-}
-
-static vm_fault_t hmm_devmem_fault(struct vm_area_struct *vma,
- unsigned long addr,
- const struct page *page,
- unsigned int flags,
- pmd_t *pmdp)
-{
- struct hmm_devmem *devmem = page->pgmap->data;
-
- return devmem->ops->fault(devmem, vma, addr, page, flags, pmdp);
-}
-
-static void hmm_devmem_free(struct page *page, void *data)
-{
- struct hmm_devmem *devmem = data;
-
- page->mapping = NULL;
-
- devmem->ops->free(devmem, page);
-}
-
-/*
- * hmm_devmem_add() - hotplug ZONE_DEVICE memory for device memory
- *
- * @ops: memory event device driver callback (see struct hmm_devmem_ops)
- * @device: device struct to bind the resource too
- * @size: size in bytes of the device memory to add
- * Returns: pointer to new hmm_devmem struct ERR_PTR otherwise
- *
- * This function first finds an empty range of physical address big enough to
- * contain the new resource, and then hotplugs it as ZONE_DEVICE memory, which
- * in turn allocates struct pages. It does not do anything beyond that; all
- * events affecting the memory will go through the various callbacks provided
- * by hmm_devmem_ops struct.
- *
- * Device driver should call this function during device initialization and
- * is then responsible of memory management. HMM only provides helpers.
- */
-struct hmm_devmem *hmm_devmem_add(const struct hmm_devmem_ops *ops,
- struct device *device,
- unsigned long size)
-{
- struct hmm_devmem *devmem;
- resource_size_t addr;
- void *result;
- int ret;
-
- dev_pagemap_get_ops();
-
- devmem = devm_kzalloc(device, sizeof(*devmem), GFP_KERNEL);
- if (!devmem)
- return ERR_PTR(-ENOMEM);
-
- init_completion(&devmem->completion);
- devmem->pfn_first = -1UL;
- devmem->pfn_last = -1UL;
- devmem->resource = NULL;
- devmem->device = device;
- devmem->ops = ops;
-
- ret = percpu_ref_init(&devmem->ref, &hmm_devmem_ref_release,
- 0, GFP_KERNEL);
- if (ret)
- return ERR_PTR(ret);
-
- size = ALIGN(size, PA_SECTION_SIZE);
- addr = min((unsigned long)iomem_resource.end,
- (1UL << MAX_PHYSMEM_BITS) - 1);
- addr = addr - size + 1UL;
-
- /*
- * FIXME add a new helper to quickly walk resource tree and find free
- * range
- *
- * FIXME what about ioport_resource resource ?
- */
- for (; addr > size && addr >= iomem_resource.start; addr -= size) {
- ret = region_intersects(addr, size, 0, IORES_DESC_NONE);
- if (ret != REGION_DISJOINT)
- continue;
-
- devmem->resource = devm_request_mem_region(device, addr, size,
- dev_name(device));
- if (!devmem->resource)
- return ERR_PTR(-ENOMEM);
- break;
- }
- if (!devmem->resource)
- return ERR_PTR(-ERANGE);
-
- devmem->resource->desc = IORES_DESC_DEVICE_PRIVATE_MEMORY;
- devmem->pfn_first = devmem->resource->start >> PAGE_SHIFT;
- devmem->pfn_last = devmem->pfn_first +
- (resource_size(devmem->resource) >> PAGE_SHIFT);
- devmem->page_fault = hmm_devmem_fault;
-
- devmem->pagemap.type = MEMORY_DEVICE_PRIVATE;
- devmem->pagemap.res = *devmem->resource;
- devmem->pagemap.page_free = hmm_devmem_free;
- devmem->pagemap.altmap_valid = false;
- devmem->pagemap.ref = &devmem->ref;
- devmem->pagemap.data = devmem;
- devmem->pagemap.kill = hmm_devmem_ref_kill;
- devmem->pagemap.cleanup = hmm_devmem_ref_exit;
-
- result = devm_memremap_pages(devmem->device, &devmem->pagemap);
- if (IS_ERR(result))
- return result;
- return devmem;
-}
-EXPORT_SYMBOL_GPL(hmm_devmem_add);
-
-struct hmm_devmem *hmm_devmem_add_resource(const struct hmm_devmem_ops *ops,
- struct device *device,
- struct resource *res)
-{
- struct hmm_devmem *devmem;
- void *result;
- int ret;
-
- if (res->desc != IORES_DESC_DEVICE_PUBLIC_MEMORY)
- return ERR_PTR(-EINVAL);
-
- dev_pagemap_get_ops();
-
- devmem = devm_kzalloc(device, sizeof(*devmem), GFP_KERNEL);
- if (!devmem)
- return ERR_PTR(-ENOMEM);
-
- init_completion(&devmem->completion);
- devmem->pfn_first = -1UL;
- devmem->pfn_last = -1UL;
- devmem->resource = res;
- devmem->device = device;
- devmem->ops = ops;
-
- ret = percpu_ref_init(&devmem->ref, &hmm_devmem_ref_release,
- 0, GFP_KERNEL);
- if (ret)
- return ERR_PTR(ret);
-
- devmem->pfn_first = devmem->resource->start >> PAGE_SHIFT;
- devmem->pfn_last = devmem->pfn_first +
- (resource_size(devmem->resource) >> PAGE_SHIFT);
- devmem->page_fault = hmm_devmem_fault;
-
- devmem->pagemap.type = MEMORY_DEVICE_PUBLIC;
- devmem->pagemap.res = *devmem->resource;
- devmem->pagemap.page_free = hmm_devmem_free;
- devmem->pagemap.altmap_valid = false;
- devmem->pagemap.ref = &devmem->ref;
- devmem->pagemap.data = devmem;
- devmem->pagemap.kill = hmm_devmem_ref_kill;
- devmem->pagemap.cleanup = hmm_devmem_ref_exit;
-
- result = devm_memremap_pages(devmem->device, &devmem->pagemap);
- if (IS_ERR(result))
- return result;
- return devmem;
-}
-EXPORT_SYMBOL_GPL(hmm_devmem_add_resource);
-
-/*
- * A device driver that wants to handle multiple devices memory through a
- * single fake device can use hmm_device to do so. This is purely a helper
- * and it is not needed to make use of any HMM functionality.
- */
-#define HMM_DEVICE_MAX 256
-
-static DECLARE_BITMAP(hmm_device_mask, HMM_DEVICE_MAX);
-static DEFINE_SPINLOCK(hmm_device_lock);
-static struct class *hmm_device_class;
-static dev_t hmm_device_devt;
-
-static void hmm_device_release(struct device *device)
-{
- struct hmm_device *hmm_device;
-
- hmm_device = container_of(device, struct hmm_device, device);
- spin_lock(&hmm_device_lock);
- clear_bit(hmm_device->minor, hmm_device_mask);
- spin_unlock(&hmm_device_lock);
-
- kfree(hmm_device);
-}
-
-struct hmm_device *hmm_device_new(void *drvdata)
-{
- struct hmm_device *hmm_device;
-
- hmm_device = kzalloc(sizeof(*hmm_device), GFP_KERNEL);
- if (!hmm_device)
- return ERR_PTR(-ENOMEM);
-
- spin_lock(&hmm_device_lock);
- hmm_device->minor = find_first_zero_bit(hmm_device_mask, HMM_DEVICE_MAX);
- if (hmm_device->minor >= HMM_DEVICE_MAX) {
- spin_unlock(&hmm_device_lock);
- kfree(hmm_device);
- return ERR_PTR(-EBUSY);
- }
- set_bit(hmm_device->minor, hmm_device_mask);
- spin_unlock(&hmm_device_lock);
-
- dev_set_name(&hmm_device->device, "hmm_device%d", hmm_device->minor);
- hmm_device->device.devt = MKDEV(MAJOR(hmm_device_devt),
- hmm_device->minor);
- hmm_device->device.release = hmm_device_release;
- dev_set_drvdata(&hmm_device->device, drvdata);
- hmm_device->device.class = hmm_device_class;
- device_initialize(&hmm_device->device);
-
- return hmm_device;
-}
-EXPORT_SYMBOL(hmm_device_new);
-
-void hmm_device_put(struct hmm_device *hmm_device)
-{
- put_device(&hmm_device->device);
-}
-EXPORT_SYMBOL(hmm_device_put);
-
-static int __init hmm_init(void)
-{
- int ret;
-
- ret = alloc_chrdev_region(&hmm_device_devt, 0,
- HMM_DEVICE_MAX,
- "hmm_device");
- if (ret)
- return ret;
-
- hmm_device_class = class_create(THIS_MODULE, "hmm_device");
- if (IS_ERR(hmm_device_class)) {
- unregister_chrdev_region(hmm_device_devt, HMM_DEVICE_MAX);
- return PTR_ERR(hmm_device_class);
- }
- return 0;
-}
-
-device_initcall(hmm_init);
-#endif /* CONFIG_DEVICE_PRIVATE || CONFIG_DEVICE_PUBLIC */
diff --git a/mm/madvise.c b/mm/madvise.c
index 628022e674a7..968df3aa069f 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -354,7 +354,7 @@ static int madvise_free_pte_range(pmd_t *pmd, unsigned long addr,
continue;
}
- page = _vm_normal_page(vma, addr, ptent, true);
+ page = vm_normal_page(vma, addr, ptent);
if (!page)
continue;
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 4f05735b02d3..249671873aa9 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -4908,7 +4908,7 @@ enum mc_target_type {
static struct page *mc_handle_present_pte(struct vm_area_struct *vma,
unsigned long addr, pte_t ptent)
{
- struct page *page = _vm_normal_page(vma, addr, ptent, true);
+ struct page *page = vm_normal_page(vma, addr, ptent);
if (!page || !page_mapped(page))
return NULL;
@@ -5109,8 +5109,8 @@ out:
* 2(MC_TARGET_SWAP): if the swap entry corresponding to this pte is a
* target for charge migration. if @target is not NULL, the entry is stored
* in target->ent.
- * 3(MC_TARGET_DEVICE): like MC_TARGET_PAGE but page is MEMORY_DEVICE_PUBLIC
- * or MEMORY_DEVICE_PRIVATE (so ZONE_DEVICE page and thus not on the lru).
+ * 3(MC_TARGET_DEVICE): like MC_TARGET_PAGE but page is MEMORY_DEVICE_PRIVATE
+ * (so ZONE_DEVICE page and thus not on the lru).
* For now we such page is charge like a regular page would be as for all
* intent and purposes it is just special memory taking the place of a
* regular page.
@@ -5144,8 +5144,7 @@ static enum mc_target_type get_mctgt_type(struct vm_area_struct *vma,
*/
if (page->mem_cgroup == mc.from) {
ret = MC_TARGET_PAGE;
- if (is_device_private_page(page) ||
- is_device_public_page(page))
+ if (is_device_private_page(page))
ret = MC_TARGET_DEVICE;
if (target)
target->page = page;
@@ -5216,8 +5215,8 @@ static int mem_cgroup_count_precharge_pte_range(pmd_t *pmd,
if (ptl) {
/*
* Note their can not be MC_TARGET_DEVICE for now as we do not
- * support transparent huge page with MEMORY_DEVICE_PUBLIC or
- * MEMORY_DEVICE_PRIVATE but this might change.
+ * support transparent huge page with MEMORY_DEVICE_PRIVATE but
+ * this might change.
*/
if (get_mctgt_type_thp(vma, addr, *pmd, NULL) == MC_TARGET_PAGE)
mc.precharge += HPAGE_PMD_NR;
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 7e08cbf3ba49..7ef849da8278 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1177,16 +1177,12 @@ static int memory_failure_dev_pagemap(unsigned long pfn, int flags,
goto unlock;
}
- switch (pgmap->type) {
- case MEMORY_DEVICE_PRIVATE:
- case MEMORY_DEVICE_PUBLIC:
+ if (pgmap->type == MEMORY_DEVICE_PRIVATE) {
/*
* TODO: Handle HMM pages which may need coordination
* with device-side memory.
*/
goto unlock;
- default:
- break;
}
/*
diff --git a/mm/memory.c b/mm/memory.c
index 53bd59579861..89325f9c6173 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -571,8 +571,8 @@ static void print_bad_pte(struct vm_area_struct *vma, unsigned long addr,
* PFNMAP mappings in order to support COWable mappings.
*
*/
-struct page *_vm_normal_page(struct vm_area_struct *vma, unsigned long addr,
- pte_t pte, bool with_public_device)
+struct page *vm_normal_page(struct vm_area_struct *vma, unsigned long addr,
+ pte_t pte)
{
unsigned long pfn = pte_pfn(pte);
@@ -585,29 +585,6 @@ struct page *_vm_normal_page(struct vm_area_struct *vma, unsigned long addr,
return NULL;
if (is_zero_pfn(pfn))
return NULL;
-
- /*
- * Device public pages are special pages (they are ZONE_DEVICE
- * pages but different from persistent memory). They behave
- * allmost like normal pages. The difference is that they are
- * not on the lru and thus should never be involve with any-
- * thing that involve lru manipulation (mlock, numa balancing,
- * ...).
- *
- * This is why we still want to return NULL for such page from
- * vm_normal_page() so that we do not have to special case all
- * call site of vm_normal_page().
- */
- if (likely(pfn <= highest_memmap_pfn)) {
- struct page *page = pfn_to_page(pfn);
-
- if (is_device_public_page(page)) {
- if (with_public_device)
- return page;
- return NULL;
- }
- }
-
if (pte_devmap(pte))
return NULL;
@@ -797,17 +774,6 @@ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
rss[mm_counter(page)]++;
} else if (pte_devmap(pte)) {
page = pte_page(pte);
-
- /*
- * Cache coherent device memory behave like regular page and
- * not like persistent memory page. For more informations see
- * MEMORY_DEVICE_CACHE_COHERENT in memory_hotplug.h
- */
- if (is_device_public_page(page)) {
- get_page(page);
- page_dup_rmap(page, false);
- rss[mm_counter(page)]++;
- }
}
out_set_pte:
@@ -1063,7 +1029,7 @@ again:
if (pte_present(ptent)) {
struct page *page;
- page = _vm_normal_page(vma, addr, ptent, true);
+ page = vm_normal_page(vma, addr, ptent);
if (unlikely(details) && page) {
/*
* unmap_shared_mapping_pages() wants to
@@ -2777,13 +2743,8 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
migration_entry_wait(vma->vm_mm, vmf->pmd,
vmf->address);
} else if (is_device_private_entry(entry)) {
- /*
- * For un-addressable device memory we call the pgmap
- * fault handler callback. The callback must migrate
- * the page back to some CPU accessible page.
- */
- ret = device_private_entry_fault(vma, vmf->address, entry,
- vmf->flags, vmf->pmd);
+ vmf->page = device_private_entry_to_page(entry);
+ ret = vmf->page->pgmap->ops->migrate_to_ram(vmf);
} else if (is_hwpoison_entry(entry)) {
ret = VM_FAULT_HWPOISON;
} else {
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index e096c987d261..6166ba5a15f3 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -557,10 +557,8 @@ void __remove_pages(struct zone *zone, unsigned long phys_start_pfn,
int sections_to_remove;
/* In the ZONE_DEVICE case device driver owns the memory region */
- if (is_dev_zone(zone)) {
- if (altmap)
- map_offset = vmem_altmap_offset(altmap);
- }
+ if (is_dev_zone(zone))
+ map_offset = vmem_altmap_offset(altmap);
clear_zone_contiguous(zone);
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index fdcb73536319..f48693f75b37 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -2098,6 +2098,7 @@ alloc_pages_vma(gfp_t gfp, int order, struct vm_area_struct *vma,
out:
return page;
}
+EXPORT_SYMBOL(alloc_pages_vma);
/**
* alloc_pages_current - Allocate pages.
diff --git a/mm/migrate.c b/mm/migrate.c
index e9594bc0d406..3445747e229d 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -246,8 +246,6 @@ static bool remove_migration_pte(struct page *page, struct vm_area_struct *vma,
if (is_device_private_page(new)) {
entry = make_device_private_entry(new, pte_write(pte));
pte = swp_entry_to_pte(entry);
- } else if (is_device_public_page(new)) {
- pte = pte_mkdevmap(pte);
}
}
@@ -381,7 +379,6 @@ static int expected_page_refs(struct address_space *mapping, struct page *page)
* ZONE_DEVICE pages.
*/
expected_count += is_device_private_page(page);
- expected_count += is_device_public_page(page);
if (mapping)
expected_count += hpage_nr_pages(page) + page_has_private(page);
@@ -994,10 +991,7 @@ static int move_to_new_page(struct page *newpage, struct page *page,
if (!PageMappingFlags(page))
page->mapping = NULL;
- if (unlikely(is_zone_device_page(newpage))) {
- if (is_device_public_page(newpage))
- flush_dcache_page(newpage);
- } else
+ if (likely(!is_zone_device_page(newpage)))
flush_dcache_page(newpage);
}
@@ -2265,7 +2259,7 @@ again:
pfn = 0;
goto next;
}
- page = _vm_normal_page(migrate->vma, addr, pte, true);
+ page = vm_normal_page(migrate->vma, addr, pte);
mpfn = migrate_pfn(pfn) | MIGRATE_PFN_MIGRATE;
mpfn |= pte_write(pte) ? MIGRATE_PFN_WRITE : 0;
}
@@ -2406,16 +2400,7 @@ static bool migrate_vma_check_page(struct page *page)
* FIXME proper solution is to rework migration_entry_wait() so
* it does not need to take a reference on page.
*/
- if (is_device_private_page(page))
- return true;
-
- /*
- * Only allow device public page to be migrated and account for
- * the extra reference count imply by ZONE_DEVICE pages.
- */
- if (!is_device_public_page(page))
- return false;
- extra++;
+ return is_device_private_page(page);
}
/* For file back page */
@@ -2665,11 +2650,6 @@ static void migrate_vma_insert_page(struct migrate_vma *migrate,
swp_entry = make_device_private_entry(page, vma->vm_flags & VM_WRITE);
entry = swp_entry_to_pte(swp_entry);
- } else if (is_device_public_page(page)) {
- entry = pte_mkold(mk_pte(page, READ_ONCE(vma->vm_page_prot)));
- if (vma->vm_flags & VM_WRITE)
- entry = pte_mkwrite(pte_mkdirty(entry));
- entry = pte_mkdevmap(entry);
}
} else {
entry = mk_pte(page, vma->vm_page_prot);
@@ -2789,7 +2769,7 @@ static void migrate_vma_pages(struct migrate_vma *migrate)
migrate->src[i] &= ~MIGRATE_PFN_MIGRATE;
continue;
}
- } else if (!is_device_public_page(newpage)) {
+ } else {
/*
* Other types of ZONE_DEVICE page are not
* supported.
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index dbd0d5cbbcbb..8fd7f45a04eb 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -5925,6 +5925,7 @@ void __ref memmap_init_zone_device(struct zone *zone,
{
unsigned long pfn, end_pfn = start_pfn + size;
struct pglist_data *pgdat = zone->zone_pgdat;
+ struct vmem_altmap *altmap = pgmap_altmap(pgmap);
unsigned long zone_idx = zone_idx(zone);
unsigned long start = jiffies;
int nid = pgdat->node_id;
@@ -5937,9 +5938,7 @@ void __ref memmap_init_zone_device(struct zone *zone,
* of the pages reserved for the memmap, so we can just jump to
* the end of that region and start processing the device pages.
*/
- if (pgmap->altmap_valid) {
- struct vmem_altmap *altmap = &pgmap->altmap;
-
+ if (altmap) {
start_pfn = altmap->base_pfn + vmem_altmap_offset(altmap);
size = end_pfn - start_pfn;
}
@@ -5959,12 +5958,12 @@ void __ref memmap_init_zone_device(struct zone *zone,
__SetPageReserved(page);
/*
- * ZONE_DEVICE pages union ->lru with a ->pgmap back
- * pointer and hmm_data. It is a bug if a ZONE_DEVICE
- * page is ever freed or placed on a driver-private list.
+ * ZONE_DEVICE pages union ->lru with a ->pgmap back pointer
+ * and zone_device_data. It is a bug if a ZONE_DEVICE page is
+ * ever freed or placed on a driver-private list.
*/
page->pgmap = pgmap;
- page->hmm_data = 0;
+ page->zone_device_data = NULL;
/*
* Mark the block movable so that blocks are reserved for
diff --git a/mm/swap.c b/mm/swap.c
index 7ede3eddc12a..607c48229a1d 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -740,15 +740,20 @@ void release_pages(struct page **pages, int nr)
if (is_huge_zero_page(page))
continue;
- /* Device public page can not be huge page */
- if (is_device_public_page(page)) {
+ if (is_zone_device_page(page)) {
if (locked_pgdat) {
spin_unlock_irqrestore(&locked_pgdat->lru_lock,
flags);
locked_pgdat = NULL;
}
- put_devmap_managed_page(page);
- continue;
+ /*
+ * ZONE_DEVICE pages that return 'false' from
+ * put_devmap_managed_page() do not require special
+ * processing, and instead, expect a call to
+ * put_page_testzero().
+ */
+ if (put_devmap_managed_page(page))
+ continue;
}
page = compound_head(page);
diff --git a/scripts/coccinelle/api/stream_open.cocci b/scripts/coccinelle/api/stream_open.cocci
index 12ce18fa6b74..df00d6619b06 100644
--- a/scripts/coccinelle/api/stream_open.cocci
+++ b/scripts/coccinelle/api/stream_open.cocci
@@ -134,6 +134,13 @@ identifier fops0.fops;
.llseek = no_llseek,
};
+@ has_noop_llseek @
+identifier fops0.fops;
+@@
+ struct file_operations fops = {
+ .llseek = noop_llseek,
+ };
+
@ has_mmap @
identifier fops0.fops;
identifier mmap_f;
@@ -180,7 +187,7 @@ identifier splice_write_f;
//
// XXX for simplicity require no .{read/write}_iter and no .splice_{read/write} for now.
// XXX maybe_steam.fops cannot be used in other rules - it gives "bad rule maybe_stream or bad variable fops".
-@ maybe_stream depends on (!has_llseek || has_no_llseek) && !has_mmap && !has_copy_file_range && !has_remap_file_range && !has_read_iter && !has_write_iter && !has_splice_read && !has_splice_write @
+@ maybe_stream depends on (!has_llseek || has_no_llseek || has_noop_llseek) && !has_mmap && !has_copy_file_range && !has_remap_file_range && !has_read_iter && !has_write_iter && !has_splice_read && !has_splice_write @
identifier fops0.fops;
@@
struct file_operations fops = {
diff --git a/tools/Makefile b/tools/Makefile
index 3dfd72ae6c1a..68defd7ecf5d 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -19,6 +19,7 @@ help:
@echo ' gpio - GPIO tools'
@echo ' hv - tools used when in Hyper-V clients'
@echo ' iio - IIO tools'
+ @echo ' intel-speed-select - Intel Speed Select tool'
@echo ' kvm_stat - top-like utility for displaying kvm statistics'
@echo ' leds - LEDs tools'
@echo ' liblockdep - user-space wrapper for kernel locking-validator'
@@ -82,7 +83,7 @@ perf: FORCE
selftests: FORCE
$(call descend,testing/$@)
-turbostat x86_energy_perf_policy: FORCE
+turbostat x86_energy_perf_policy intel-speed-select: FORCE
$(call descend,power/x86/$@)
tmon: FORCE
@@ -115,7 +116,7 @@ liblockdep_install:
selftests_install:
$(call descend,testing/$(@:_install=),install)
-turbostat_install x86_energy_perf_policy_install:
+turbostat_install x86_energy_perf_policy_install intel-speed-select_install:
$(call descend,power/x86/$(@:_install=),install)
tmon_install:
@@ -132,7 +133,7 @@ install: acpi_install cgroup_install cpupower_install gpio_install \
perf_install selftests_install turbostat_install usb_install \
virtio_install vm_install bpf_install x86_energy_perf_policy_install \
tmon_install freefall_install objtool_install kvm_stat_install \
- wmi_install pci_install debugging_install
+ wmi_install pci_install debugging_install intel-speed-select_install
acpi_clean:
$(call descend,power/acpi,clean)
@@ -162,7 +163,7 @@ perf_clean:
selftests_clean:
$(call descend,testing/$(@:_clean=),clean)
-turbostat_clean x86_energy_perf_policy_clean:
+turbostat_clean x86_energy_perf_policy_clean intel-speed-select_clean:
$(call descend,power/x86/$(@:_clean=),clean)
tmon_clean:
@@ -178,6 +179,7 @@ clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \
perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
- gpio_clean objtool_clean leds_clean wmi_clean pci_clean firmware_clean debugging_clean
+ gpio_clean objtool_clean leds_clean wmi_clean pci_clean firmware_clean debugging_clean \
+ intel-speed-select_clean
.PHONY: FORCE
diff --git a/tools/include/linux/zalloc.h b/tools/include/linux/zalloc.h
new file mode 100644
index 000000000000..81099c84043f
--- /dev/null
+++ b/tools/include/linux/zalloc.h
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: LGPL-2.1
+#ifndef __TOOLS_LINUX_ZALLOC_H
+#define __TOOLS_LINUX_ZALLOC_H
+
+#include <stddef.h>
+
+void *zalloc(size_t size);
+void __zfree(void **ptr);
+
+#define zfree(ptr) __zfree((void **)(ptr))
+
+#endif // __TOOLS_LINUX_ZALLOC_H
diff --git a/tools/lib/zalloc.c b/tools/lib/zalloc.c
new file mode 100644
index 000000000000..9c856d59f56e
--- /dev/null
+++ b/tools/lib/zalloc.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: LGPL-2.1
+
+#include <stdlib.h>
+#include <linux/zalloc.h>
+
+void *zalloc(size_t size)
+{
+ return calloc(1, size);
+}
+
+void __zfree(void **ptr)
+{
+ free(*ptr);
+ *ptr = NULL;
+}
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST
index 6a5de44b2de9..70f1ff4e2eb4 100644
--- a/tools/perf/MANIFEST
+++ b/tools/perf/MANIFEST
@@ -18,3 +18,4 @@ tools/lib/find_bit.c
tools/lib/bitmap.c
tools/lib/str_error_r.c
tools/lib/vsprintf.c
+tools/lib/zalloc.c
diff --git a/tools/perf/arch/arm/annotate/instructions.c b/tools/perf/arch/arm/annotate/instructions.c
index f64516d5b23e..c7d1a69b894f 100644
--- a/tools/perf/arch/arm/annotate/instructions.c
+++ b/tools/perf/arch/arm/annotate/instructions.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
+#include <linux/zalloc.h>
#include <sys/types.h>
#include <regex.h>
diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c
index 1ce6bdbda561..02014740a1aa 100644
--- a/tools/perf/arch/arm/util/auxtrace.c
+++ b/tools/perf/arch/arm/util/auxtrace.c
@@ -6,6 +6,7 @@
#include <stdbool.h>
#include <linux/coresight-pmu.h>
+#include <linux/zalloc.h>
#include "../../util/auxtrace.h"
#include "../../util/evlist.h"
diff --git a/tools/perf/arch/arm/util/cs-etm.c b/tools/perf/arch/arm/util/cs-etm.c
index 2b83cc8e4796..4208974c24f8 100644
--- a/tools/perf/arch/arm/util/cs-etm.c
+++ b/tools/perf/arch/arm/util/cs-etm.c
@@ -12,6 +12,7 @@
#include <linux/kernel.h>
#include <linux/log2.h>
#include <linux/types.h>
+#include <linux/zalloc.h>
#include "cs-etm.h"
#include "../../perf.h"
diff --git a/tools/perf/arch/arm64/util/arm-spe.c b/tools/perf/arch/arm64/util/arm-spe.c
index 5ccfce87e693..2c009aa74633 100644
--- a/tools/perf/arch/arm64/util/arm-spe.c
+++ b/tools/perf/arch/arm64/util/arm-spe.c
@@ -8,6 +8,7 @@
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/log2.h>
+#include <linux/zalloc.h>
#include <time.h>
#include "../../util/cpumap.h"
diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c
index f3824ca7c20b..1a9e22f78c22 100644
--- a/tools/perf/arch/common.c
+++ b/tools/perf/arch/common.c
@@ -1,9 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
+#include <stdlib.h>
#include "common.h"
#include "../util/env.h"
-#include "../util/util.h"
#include "../util/debug.h"
+#include <linux/zalloc.h>
const char *const arc_triplets[] = {
"arc-linux-",
diff --git a/tools/perf/arch/powerpc/util/perf_regs.c b/tools/perf/arch/powerpc/util/perf_regs.c
index 34d5134681d9..f14102b85509 100644
--- a/tools/perf/arch/powerpc/util/perf_regs.c
+++ b/tools/perf/arch/powerpc/util/perf_regs.c
@@ -2,12 +2,14 @@
#include <errno.h>
#include <string.h>
#include <regex.h>
+#include <linux/zalloc.h>
#include "../../perf.h"
-#include "../../util/util.h"
#include "../../util/perf_regs.h"
#include "../../util/debug.h"
+#include <linux/kernel.h>
+
const struct sample_reg sample_reg_masks[] = {
SMPL_REG(r0, PERF_REG_POWERPC_R0),
SMPL_REG(r1, PERF_REG_POWERPC_R1),
diff --git a/tools/perf/arch/s390/util/auxtrace.c b/tools/perf/arch/s390/util/auxtrace.c
index 44c857388897..0fe1be93f375 100644
--- a/tools/perf/arch/s390/util/auxtrace.c
+++ b/tools/perf/arch/s390/util/auxtrace.c
@@ -3,6 +3,7 @@
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/log2.h>
+#include <linux/zalloc.h>
#include "../../util/evlist.h"
#include "../../util/auxtrace.h"
diff --git a/tools/perf/arch/s390/util/header.c b/tools/perf/arch/s390/util/header.c
index a25896135abe..8b0b018d896a 100644
--- a/tools/perf/arch/s390/util/header.c
+++ b/tools/perf/arch/s390/util/header.c
@@ -12,9 +12,10 @@
#include <stdio.h>
#include <string.h>
#include <linux/ctype.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
#include "../../util/header.h"
-#include "../../util/util.h"
#define SYSINFO_MANU "Manufacturer:"
#define SYSINFO_TYPE "Type:"
diff --git a/tools/perf/arch/x86/util/event.c b/tools/perf/arch/x86/util/event.c
index 675a0213044d..a3a0b6884779 100644
--- a/tools/perf/arch/x86/util/event.c
+++ b/tools/perf/arch/x86/util/event.c
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/types.h>
#include <linux/string.h>
+#include <linux/zalloc.h>
#include "../../util/machine.h"
#include "../../util/tool.h"
#include "../../util/map.h"
-#include "../../util/util.h"
#include "../../util/debug.h"
#if defined(__x86_64__)
diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c
index e6d4d9591c79..ec5c1bb84095 100644
--- a/tools/perf/arch/x86/util/intel-bts.c
+++ b/tools/perf/arch/x86/util/intel-bts.c
@@ -9,12 +9,12 @@
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/log2.h>
+#include <linux/zalloc.h>
#include "../../util/cpumap.h"
#include "../../util/evsel.h"
#include "../../util/evlist.h"
#include "../../util/session.h"
-#include "../../util/util.h"
#include "../../util/pmu.h"
#include "../../util/debug.h"
#include "../../util/tsc.h"
diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c
index 9804098dcefb..609088c01e3a 100644
--- a/tools/perf/arch/x86/util/intel-pt.c
+++ b/tools/perf/arch/x86/util/intel-pt.c
@@ -10,6 +10,7 @@
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/log2.h>
+#include <linux/zalloc.h>
#include <cpuid.h>
#include "../../perf.h"
@@ -25,7 +26,6 @@
#include "../../util/auxtrace.h"
#include "../../util/tsc.h"
#include "../../util/intel-pt.h"
-#include "../../util/util.h"
#define KiB(x) ((x) * 1024)
#define MiB(x) ((x) * 1024 * 1024)
diff --git a/tools/perf/arch/x86/util/perf_regs.c b/tools/perf/arch/x86/util/perf_regs.c
index 3666c0076df9..0d7b77ff0ae6 100644
--- a/tools/perf/arch/x86/util/perf_regs.c
+++ b/tools/perf/arch/x86/util/perf_regs.c
@@ -2,9 +2,9 @@
#include <errno.h>
#include <string.h>
#include <regex.h>
+#include <linux/zalloc.h>
#include "../../perf.h"
-#include "../../util/util.h"
#include "../../util/perf_regs.h"
#include "../../util/debug.h"
diff --git a/tools/perf/bench/futex-hash.c b/tools/perf/bench/futex-hash.c
index 9aa3a674829b..a80797763e1f 100644
--- a/tools/perf/bench/futex-hash.c
+++ b/tools/perf/bench/futex-hash.c
@@ -18,6 +18,7 @@
#include <stdlib.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
+#include <linux/zalloc.h>
#include <sys/time.h>
#include "../util/stat.h"
@@ -214,7 +215,7 @@ int bench_futex_hash(int argc, const char **argv)
&worker[i].futex[nfutexes-1], t);
}
- free(worker[i].futex);
+ zfree(&worker[i].futex);
}
print_summary();
diff --git a/tools/perf/bench/futex-lock-pi.c b/tools/perf/bench/futex-lock-pi.c
index 8e9c4753e304..d02330a69745 100644
--- a/tools/perf/bench/futex-lock-pi.c
+++ b/tools/perf/bench/futex-lock-pi.c
@@ -12,6 +12,7 @@
#include <subcmd/parse-options.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
+#include <linux/zalloc.h>
#include <errno.h>
#include "bench.h"
#include "futex.h"
@@ -217,7 +218,7 @@ int bench_futex_lock_pi(int argc, const char **argv)
worker[i].tid, worker[i].futex, t);
if (multi)
- free(worker[i].futex);
+ zfree(&worker[i].futex);
}
print_summary();
diff --git a/tools/perf/bench/mem-functions.c b/tools/perf/bench/mem-functions.c
index 0251dd348124..64dc994c72ea 100644
--- a/tools/perf/bench/mem-functions.c
+++ b/tools/perf/bench/mem-functions.c
@@ -9,7 +9,6 @@
#include "debug.h"
#include "../perf.h"
-#include "../util/util.h"
#include <subcmd/parse-options.h>
#include "../util/header.h"
#include "../util/cloexec.h"
@@ -24,6 +23,7 @@
#include <sys/time.h>
#include <errno.h>
#include <linux/time64.h>
+#include <linux/zalloc.h>
#define K 1024
diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c
index a7784554a80d..a640ca7aaada 100644
--- a/tools/perf/bench/numa.c
+++ b/tools/perf/bench/numa.c
@@ -11,7 +11,6 @@
#include "../perf.h"
#include "../builtin.h"
-#include "../util/util.h"
#include <subcmd/parse-options.h>
#include "../util/cloexec.h"
@@ -35,6 +34,7 @@
#include <linux/kernel.h>
#include <linux/time64.h>
#include <linux/numa.h>
+#include <linux/zalloc.h>
#include <numa.h>
#include <numaif.h>
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 77deb3a40596..e0aa14faf2b5 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -8,11 +8,11 @@
*/
#include "builtin.h"
-#include "util/util.h"
#include "util/color.h"
#include <linux/list.h>
#include "util/cache.h"
#include <linux/rbtree.h>
+#include <linux/zalloc.h>
#include "util/symbol.h"
#include "perf.h"
diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c
index 334c77ffc1d9..b8e7c38ef221 100644
--- a/tools/perf/builtin-bench.c
+++ b/tools/perf/builtin-bench.c
@@ -17,7 +17,6 @@
* epoll ... Event poll performance
*/
#include "perf.h"
-#include "util/util.h"
#include <subcmd/parse-options.h>
#include "builtin.h"
#include "bench/bench.h"
@@ -26,6 +25,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
+#include <linux/zalloc.h>
typedef int (*bench_fn_t)(int argc, const char **argv);
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index 9e6cc868bdb4..e3776f5c2e01 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -15,9 +15,9 @@
#include <linux/compiler.h>
#include <linux/kernel.h>
#include <linux/stringify.h>
+#include <linux/zalloc.h>
#include <asm/bug.h>
#include <sys/param.h>
-#include "util.h"
#include "debug.h"
#include "builtin.h"
#include <subcmd/parse-options.h>
diff --git a/tools/perf/builtin-config.c b/tools/perf/builtin-config.c
index d76f831f94c7..6c1284c87aaa 100644
--- a/tools/perf/builtin-config.c
+++ b/tools/perf/builtin-config.c
@@ -15,6 +15,7 @@
#include "util/debug.h"
#include "util/config.h"
#include <linux/string.h>
+#include <stdlib.h>
static bool use_system_config, use_user_config;
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index f924b46910b5..f6f5dd15bea7 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -16,12 +16,12 @@
#include "util/tool.h"
#include "util/sort.h"
#include "util/symbol.h"
-#include "util/util.h"
#include "util/data.h"
#include "util/config.h"
#include "util/time-utils.h"
#include "util/annotate.h"
#include "util/map.h"
+#include <linux/zalloc.h>
#include <errno.h>
#include <inttypes.h>
diff --git a/tools/perf/builtin-ftrace.c b/tools/perf/builtin-ftrace.c
index 9c228c55e1fb..66d5a6658daf 100644
--- a/tools/perf/builtin-ftrace.c
+++ b/tools/perf/builtin-ftrace.c
@@ -431,7 +431,7 @@ static void delete_filter_func(struct list_head *head)
struct filter_entry *pos, *tmp;
list_for_each_entry_safe(pos, tmp, head, list) {
- list_del(&pos->list);
+ list_del_init(&pos->list);
free(pos);
}
}
diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c
index 3d29d0524a89..a83af92fb0d1 100644
--- a/tools/perf/builtin-help.c
+++ b/tools/perf/builtin-help.c
@@ -14,8 +14,10 @@
#include <subcmd/help.h>
#include "util/debug.h"
#include <linux/kernel.h>
+#include <linux/zalloc.h>
#include <errno.h>
#include <stdio.h>
+#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index 8e0e06d3edfc..f4591a1438b4 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -224,7 +224,7 @@ static int perf_event__repipe_sample(struct perf_tool *tool,
struct perf_evsel *evsel,
struct machine *machine)
{
- if (evsel->handler) {
+ if (evsel && evsel->handler) {
inject_handler f = evsel->handler;
return f(tool, event, sample, evsel, machine);
}
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 9bd3829de76d..9e5e60898083 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -4,7 +4,6 @@
#include "util/evlist.h"
#include "util/evsel.h"
-#include "util/util.h"
#include "util/config.h"
#include "util/map.h"
#include "util/symbol.h"
@@ -26,6 +25,7 @@
#include <linux/kernel.h>
#include <linux/rbtree.h>
#include <linux/string.h>
+#include <linux/zalloc.h>
#include <errno.h>
#include <inttypes.h>
#include <locale.h>
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
index dbb6f737a3e2..b33c83489120 100644
--- a/tools/perf/builtin-kvm.c
+++ b/tools/perf/builtin-kvm.c
@@ -5,7 +5,6 @@
#include "util/evsel.h"
#include "util/evlist.h"
#include "util/term.h"
-#include "util/util.h"
#include "util/cache.h"
#include "util/symbol.h"
#include "util/thread.h"
@@ -32,6 +31,7 @@
#include <linux/kernel.h>
#include <linux/time64.h>
+#include <linux/zalloc.h>
#include <errno.h>
#include <inttypes.h>
#include <poll.h>
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index b9810a8d350a..574e30ec6d7c 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -6,7 +6,6 @@
#include "util/evlist.h"
#include "util/evsel.h"
-#include "util/util.h"
#include "util/cache.h"
#include "util/symbol.h"
#include "util/thread.h"
@@ -30,6 +29,7 @@
#include <linux/list.h>
#include <linux/hash.h>
#include <linux/kernel.h>
+#include <linux/zalloc.h>
static struct perf_session *session;
@@ -454,7 +454,7 @@ broken:
/* broken lock sequence, discard it */
ls->discard = 1;
bad_hist[BROKEN_ACQUIRE]++;
- list_del(&seq->list);
+ list_del_init(&seq->list);
free(seq);
goto end;
default:
@@ -515,7 +515,7 @@ static int report_lock_acquired_event(struct perf_evsel *evsel,
/* broken lock sequence, discard it */
ls->discard = 1;
bad_hist[BROKEN_ACQUIRED]++;
- list_del(&seq->list);
+ list_del_init(&seq->list);
free(seq);
goto end;
default:
@@ -570,7 +570,7 @@ static int report_lock_contended_event(struct perf_evsel *evsel,
/* broken lock sequence, discard it */
ls->discard = 1;
bad_hist[BROKEN_CONTENDED]++;
- list_del(&seq->list);
+ list_del_init(&seq->list);
free(seq);
goto end;
default:
@@ -639,7 +639,7 @@ static int report_lock_release_event(struct perf_evsel *evsel,
ls->nr_release++;
free_seq:
- list_del(&seq->list);
+ list_del_init(&seq->list);
free(seq);
end:
return 0;
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 8bb124e55c6d..6418782951a4 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -19,7 +19,6 @@
#include "perf.h"
#include "builtin.h"
#include "namespaces.h"
-#include "util/util.h"
#include "util/strlist.h"
#include "util/strfilter.h"
#include "util/symbol.h"
@@ -28,6 +27,7 @@
#include "util/probe-finder.h"
#include "util/probe-event.h"
#include "util/probe-file.h"
+#include <linux/zalloc.h>
#define DEFAULT_VAR_FILTER "!__k???tab_* & !__crc_*"
#define DEFAULT_FUNC_FILTER "!_*"
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index dca55997934e..8779cee58185 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -11,7 +11,6 @@
#include "perf.h"
#include "util/build-id.h"
-#include "util/util.h"
#include <subcmd/parse-options.h>
#include "util/parse-events.h"
#include "util/config.h"
@@ -54,6 +53,7 @@
#include <sys/mman.h>
#include <sys/wait.h>
#include <linux/time64.h>
+#include <linux/zalloc.h>
struct switch_output {
bool enabled;
@@ -1110,7 +1110,7 @@ record__switch_output(struct record *rec, bool at_exit)
rec->switch_output.cur_file = n;
if (rec->switch_output.filenames[n]) {
remove(rec->switch_output.filenames[n]);
- free(rec->switch_output.filenames[n]);
+ zfree(&rec->switch_output.filenames[n]);
}
rec->switch_output.filenames[n] = new_filename;
} else {
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index aef59f318a67..abf0b9b8f566 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -8,7 +8,6 @@
*/
#include "builtin.h"
-#include "util/util.h"
#include "util/config.h"
#include "util/annotate.h"
@@ -16,6 +15,7 @@
#include <linux/list.h>
#include <linux/rbtree.h>
#include <linux/err.h>
+#include <linux/zalloc.h>
#include "util/map.h"
#include "util/symbol.h"
#include "util/callchain.h"
@@ -298,7 +298,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 = evsel ? perf_evsel__name(evsel) : "unknown";
+ const char *name = perf_evsel__name(evsel);
int err = perf_read_values_add_value(&rep->show_threads_values,
event->read.pid, event->read.tid,
evsel->idx,
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 1519989961ff..56d1907b1215 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -2,7 +2,6 @@
#include "builtin.h"
#include "perf.h"
-#include "util/util.h"
#include "util/evlist.h"
#include "util/cache.h"
#include "util/evsel.h"
@@ -26,6 +25,7 @@
#include <linux/kernel.h>
#include <linux/log2.h>
+#include <linux/zalloc.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <inttypes.h>
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index 2f6232f1bfdc..79367087bd18 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -14,7 +14,6 @@
#include "util/symbol.h"
#include "util/thread.h"
#include "util/trace-event.h"
-#include "util/util.h"
#include "util/evlist.h"
#include "util/evsel.h"
#include "util/sort.h"
@@ -34,6 +33,7 @@
#include <linux/kernel.h>
#include <linux/stringify.h>
#include <linux/time64.h>
+#include <linux/zalloc.h>
#include <sys/utsname.h>
#include "asm/bug.h"
#include "util/mem-events.h"
@@ -3752,7 +3752,8 @@ int cmd_script(int argc, const char **argv)
goto out_delete;
uname(&uts);
- if (!strcmp(uts.machine, session->header.env.arch) ||
+ if (data.is_pipe || /* assume pipe_mode indicates native_arch */
+ !strcmp(uts.machine, session->header.env.arch) ||
(!strcmp(uts.machine, "x86_64") &&
!strcmp(session->header.env.arch, "i386")))
native_arch = true;
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index e5e19b461061..b55a534b4de0 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -43,7 +43,6 @@
#include "perf.h"
#include "builtin.h"
#include "util/cgroup.h"
-#include "util/util.h"
#include <subcmd/parse-options.h>
#include "util/parse-events.h"
#include "util/pmu.h"
@@ -67,6 +66,7 @@
#include "asm/bug.h"
#include <linux/time64.h>
+#include <linux/zalloc.h>
#include <api/fs/fs.h>
#include <errno.h>
#include <signal.h>
@@ -1349,8 +1349,8 @@ static int add_default_attributes(void)
fprintf(stderr,
"Cannot set up top down events %s: %d\n",
str, err);
- free(str);
parse_events_print_error(&errinfo, str);
+ free(str);
return -1;
}
} else {
@@ -1586,7 +1586,7 @@ static void runtime_stat_delete(struct perf_stat_config *config)
for (i = 0; i < config->stats_num; i++)
runtime_stat__exit(&config->stats[i]);
- free(config->stats);
+ zfree(&config->stats);
}
static const char * const stat_report_usage[] = {
@@ -2003,7 +2003,7 @@ int cmd_stat(int argc, const char **argv)
perf_stat__exit_aggr_mode();
perf_evlist__free_stats(evsel_list);
out:
- free(stat_config.walltime_run);
+ zfree(&stat_config.walltime_run);
if (smi_cost && smi_reset)
sysfs__write_int(FREEZE_ON_SMI_PATH, 0);
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 145a19668114..4bde3fa245d1 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -13,9 +13,6 @@
#include <traceevent/event-parse.h>
#include "builtin.h"
-
-#include "util/util.h"
-
#include "util/color.h"
#include <linux/list.h>
#include "util/cache.h"
@@ -24,6 +21,7 @@
#include <linux/kernel.h>
#include <linux/rbtree.h>
#include <linux/time64.h>
+#include <linux/zalloc.h>
#include "util/symbol.h"
#include "util/thread.h"
#include "util/callchain.h"
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 6d40a4ef58c5..b46b3c9f57a0 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -101,7 +101,7 @@ static void perf_top__resize(struct perf_top *top)
static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he)
{
- struct perf_evsel *evsel = hists_to_evsel(he->hists);
+ struct perf_evsel *evsel;
struct symbol *sym;
struct annotation *notes;
struct map *map;
@@ -110,6 +110,8 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he)
if (!he || !he->ms.sym)
return -1;
+ evsel = hists_to_evsel(he->hists);
+
sym = he->ms.sym;
map = he->ms.map;
@@ -226,7 +228,7 @@ static void perf_top__record_precise_ip(struct perf_top *top,
static void perf_top__show_details(struct perf_top *top)
{
struct hist_entry *he = top->sym_filter_entry;
- struct perf_evsel *evsel = hists_to_evsel(he->hists);
+ struct perf_evsel *evsel;
struct annotation *notes;
struct symbol *symbol;
int more;
@@ -234,6 +236,8 @@ static void perf_top__show_details(struct perf_top *top)
if (!he)
return;
+ evsel = hists_to_evsel(he->hists);
+
symbol = he->ms.sym;
notes = symbol__annotation(symbol);
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index d0eb7224dd36..1aa2ed096f65 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -61,6 +61,7 @@
#include <linux/random.h>
#include <linux/stringify.h>
#include <linux/time64.h>
+#include <linux/zalloc.h>
#include <fcntl.h>
#include <sys/sysmacros.h>
@@ -1038,10 +1039,10 @@ static struct thread_trace *thread_trace__new(void)
{
struct thread_trace *ttrace = zalloc(sizeof(struct thread_trace));
- if (ttrace)
+ if (ttrace) {
ttrace->files.max = -1;
-
- ttrace->syscall_stats = intlist__new(NULL);
+ ttrace->syscall_stats = intlist__new(NULL);
+ }
return ttrace;
}
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 2123b3cc4dcf..97e2628ea5dd 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -18,7 +18,6 @@
#include "util/bpf-loader.h"
#include "util/debug.h"
#include "util/event.h"
-#include "util/util.h"
#include <api/fs/fs.h>
#include <api/fs/tracing_path.h>
#include <errno.h>
@@ -30,6 +29,7 @@
#include <sys/stat.h>
#include <unistd.h>
#include <linux/kernel.h>
+#include <linux/zalloc.h>
const char perf_usage_string[] =
"perf [--version] [--help] [OPTIONS] COMMAND [ARGS]";
diff --git a/tools/perf/pmu-events/jevents.c b/tools/perf/pmu-events/jevents.c
index 287a6f10ca48..1a91a197cafb 100644
--- a/tools/perf/pmu-events/jevents.c
+++ b/tools/perf/pmu-events/jevents.c
@@ -407,7 +407,7 @@ static void free_arch_std_events(void)
list_for_each_entry_safe(es, next, &arch_std_events, list) {
FOR_ALL_EVENT_STRUCT_FIELDS(FREE_EVENT_FIELD);
- list_del(&es->list);
+ list_del_init(&es->list);
free(es);
}
}
diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py
index 4447f0d7c754..92713d93e956 100644
--- a/tools/perf/scripts/python/export-to-postgresql.py
+++ b/tools/perf/scripts/python/export-to-postgresql.py
@@ -898,11 +898,11 @@ def trace_end():
if is_table_empty("ptwrite"):
drop("ptwrite")
if is_table_empty("mwait") and is_table_empty("pwre") and is_table_empty("exstop") and is_table_empty("pwrx"):
+ do_query(query, 'DROP VIEW power_events_view');
drop("mwait")
drop("pwre")
drop("exstop")
drop("pwrx")
- do_query(query, 'DROP VIEW power_events_view');
if is_table_empty("cbr"):
drop("cbr")
diff --git a/tools/perf/scripts/python/export-to-sqlite.py b/tools/perf/scripts/python/export-to-sqlite.py
index 3222a83f4184..021326c46285 100644
--- a/tools/perf/scripts/python/export-to-sqlite.py
+++ b/tools/perf/scripts/python/export-to-sqlite.py
@@ -608,11 +608,11 @@ def trace_end():
if is_table_empty("ptwrite"):
drop("ptwrite")
if is_table_empty("mwait") and is_table_empty("pwre") and is_table_empty("exstop") and is_table_empty("pwrx"):
+ do_query(query, 'DROP VIEW power_events_view');
drop("mwait")
drop("pwre")
drop("exstop")
drop("pwrx")
- do_query(query, 'DROP VIEW power_events_view');
if is_table_empty("cbr"):
drop("cbr")
diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c
index 077c306c1cae..f33709a79335 100644
--- a/tools/perf/tests/dwarf-unwind.c
+++ b/tools/perf/tests/dwarf-unwind.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
#include <linux/types.h>
+#include <linux/zalloc.h>
#include <inttypes.h>
#include <unistd.h>
#include "tests.h"
@@ -115,8 +116,8 @@ noinline int test_dwarf_unwind__thread(struct thread *thread)
}
out:
- free(sample.user_stack.data);
- free(sample.user_regs.regs);
+ zfree(&sample.user_stack.data);
+ zfree(&sample.user_regs.regs);
return err;
}
diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 9acc1e80b936..ee1d88650e69 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -3,6 +3,7 @@
#include "util/expr.h"
#include "tests.h"
#include <stdlib.h>
+#include <linux/zalloc.h>
static int test(struct parse_ctx *ctx, const char *e, double val2)
{
@@ -58,7 +59,7 @@ int test__expr(struct test *t __maybe_unused, int subtest __maybe_unused)
TEST_ASSERT_VAL("find other", other[3] == NULL);
for (i = 0; i < num_other; i++)
- free((void *)other[i]);
+ zfree(&other[i]);
free((void *)other);
return 0;
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
index a039f93199e5..ca5a5f94ce79 100644
--- a/tools/perf/tests/llvm.c
+++ b/tools/perf/tests/llvm.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
+#include <stdlib.h>
#include <bpf/libbpf.h>
#include <util/llvm-utils.h>
#include <util/cache.h>
diff --git a/tools/perf/tests/mem2node.c b/tools/perf/tests/mem2node.c
index d23ff1b68eba..520cc91af256 100644
--- a/tools/perf/tests/mem2node.c
+++ b/tools/perf/tests/mem2node.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
#include <linux/bitmap.h>
+#include <linux/zalloc.h>
#include "cpumap.h"
#include "mem2node.h"
#include "tests.h"
@@ -67,7 +68,7 @@ int test__mem2node(struct test *t __maybe_unused, int subtest __maybe_unused)
T("failed: mem2node__node", -1 == mem2node__node(&map, 0x1050));
for (i = 0; i < ARRAY_SIZE(nodes); i++)
- free(nodes[i].set);
+ zfree(&nodes[i].set);
mem2node__exit(&map);
return 0;
diff --git a/tools/perf/tests/mmap-thread-lookup.c b/tools/perf/tests/mmap-thread-lookup.c
index ba87e6e8d18c..0a4301a5155c 100644
--- a/tools/perf/tests/mmap-thread-lookup.c
+++ b/tools/perf/tests/mmap-thread-lookup.c
@@ -53,7 +53,7 @@ static void *thread_fn(void *arg)
{
struct thread_data *td = arg;
ssize_t ret;
- int go;
+ int go = 0;
if (thread_init(td))
return NULL;
diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c
index 236ce0d6c826..361714e2583c 100644
--- a/tools/perf/tests/sample-parsing.c
+++ b/tools/perf/tests/sample-parsing.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdbool.h>
#include <inttypes.h>
+#include <stdlib.h>
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/types.h>
diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c
index 9b5be51e5e7b..6cdab5f4812a 100644
--- a/tools/perf/tests/switch-tracking.c
+++ b/tools/perf/tests/switch-tracking.c
@@ -4,6 +4,7 @@
#include <errno.h>
#include <time.h>
#include <stdlib.h>
+#include <linux/zalloc.h>
#include "parse-events.h"
#include "evlist.h"
@@ -237,7 +238,7 @@ static void free_event_nodes(struct list_head *events)
while (!list_empty(events)) {
node = list_entry(events->next, struct event_node, list);
- list_del(&node->list);
+ list_del_init(&node->list);
free(node);
}
}
diff --git a/tools/perf/tests/thread-map.c b/tools/perf/tests/thread-map.c
index 4de1939b58ba..ccc17aced49e 100644
--- a/tools/perf/tests/thread-map.c
+++ b/tools/perf/tests/thread-map.c
@@ -6,6 +6,7 @@
#include "tests.h"
#include "thread_map.h"
#include "debug.h"
+#include <linux/zalloc.h>
#define NAME (const char *) "perf"
#define NAMEUL (unsigned long) NAME
@@ -133,7 +134,7 @@ int test__thread_map_remove(struct test *test __maybe_unused, int subtest __mayb
thread_map__remove(threads, 0));
for (i = 0; i < threads->nr; i++)
- free(threads->map[i].comm);
+ zfree(&threads->map[i].comm);
free(threads);
return 0;
diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c
index f101576d1c72..5e8834fc7dec 100644
--- a/tools/perf/tests/vmlinux-kallsyms.c
+++ b/tools/perf/tests/vmlinux-kallsyms.c
@@ -3,6 +3,7 @@
#include <linux/rbtree.h>
#include <inttypes.h>
#include <string.h>
+#include <stdlib.h>
#include "map.h"
#include "symbol.h"
#include "util.h"
diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c
index 55ff05a46e0b..f80c51d53565 100644
--- a/tools/perf/ui/browser.c
+++ b/tools/perf/ui/browser.c
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
-#include "../util.h"
#include "../string2.h"
#include "../config.h"
#include "../../perf.h"
@@ -17,6 +16,7 @@
#include "keysyms.h"
#include "../color.h"
#include <linux/ctype.h>
+#include <linux/zalloc.h>
static int ui_browser__percent_color(struct ui_browser *browser,
double percent, bool current)
diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h
index aa5932e1d62e..dc1444136658 100644
--- a/tools/perf/ui/browser.h
+++ b/tools/perf/ui/browser.h
@@ -4,6 +4,7 @@
#include <linux/types.h>
#include <stdarg.h>
+#include <sys/types.h>
#define HE_COLORSET_TOP 50
#define HE_COLORSET_MEDIUM 51
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index b0d089a95dac..e67880bf1efe 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
-#include "../../util/util.h"
#include "../browser.h"
#include "../helpline.h"
#include "../ui.h"
@@ -15,6 +14,7 @@
#include <pthread.h>
#include <linux/kernel.h>
#include <linux/string.h>
+#include <linux/zalloc.h>
#include <sys/ttydefaults.h>
#include <asm/bug.h>
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 33e67aa91347..a94eb0755e8b 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -9,6 +9,7 @@
#include <linux/string.h>
#include <sys/ttydefaults.h>
#include <linux/time64.h>
+#include <linux/zalloc.h>
#include "../../util/callchain.h"
#include "../../util/evsel.h"
@@ -18,7 +19,6 @@
#include "../../util/symbol.h"
#include "../../util/pstack.h"
#include "../../util/sort.h"
-#include "../../util/util.h"
#include "../../util/top.h"
#include "../../util/thread.h"
#include "../../arch/common.h"
@@ -639,7 +639,11 @@ int hist_browser__run(struct hist_browser *browser, const char *help,
switch (key) {
case K_TIMER: {
u64 nr_entries;
- hbt->timer(hbt->arg);
+
+ WARN_ON_ONCE(!hbt);
+
+ if (hbt)
+ hbt->timer(hbt->arg);
if (hist_browser__has_filter(browser) ||
symbol_conf.report_hierarchy)
@@ -2821,7 +2825,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
{
struct hists *hists = evsel__hists(evsel);
struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
- struct branch_info *bi;
+ struct branch_info *bi = NULL;
#define MAX_OPTIONS 16
char *options[MAX_OPTIONS];
struct popup_action actions[MAX_OPTIONS];
@@ -3087,7 +3091,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
goto skip_annotation;
if (sort__mode == SORT_MODE__BRANCH) {
- bi = browser->he_selection->branch_info;
+
+ if (browser->he_selection)
+ bi = browser->he_selection->branch_info;
if (bi == NULL)
goto skip_annotation;
@@ -3271,7 +3277,8 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
switch (key) {
case K_TIMER:
- hbt->timer(hbt->arg);
+ if (hbt)
+ hbt->timer(hbt->arg);
if (!menu->lost_events_warned &&
menu->lost_events &&
diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c
index 5f6529c9eb8e..4c545b92e20d 100644
--- a/tools/perf/ui/browsers/map.c
+++ b/tools/perf/ui/browsers/map.c
@@ -2,6 +2,7 @@
#include <elf.h>
#include <inttypes.h>
#include <sys/ttydefaults.h>
+#include <stdlib.h>
#include <string.h>
#include <linux/bitops.h>
#include "../../util/util.h"
diff --git a/tools/perf/ui/browsers/res_sample.c b/tools/perf/ui/browsers/res_sample.c
index c0dd73176d42..8aa3547bb9ff 100644
--- a/tools/perf/ui/browsers/res_sample.c
+++ b/tools/perf/ui/browsers/res_sample.c
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
/* Display a menu with individual samples to browse with perf script */
-#include "util.h"
#include "hist.h"
#include "evsel.h"
#include "hists.h"
@@ -8,6 +7,7 @@
#include "config.h"
#include "time-utils.h"
#include <linux/time64.h>
+#include <linux/zalloc.h>
static u64 context_len = 10 * NSEC_PER_MSEC;
@@ -46,14 +46,14 @@ int res_sample_browse(struct res_sample *res_samples, int num_res,
if (asprintf(&names[i], "%s: CPU %d tid %d", tbuf,
res_samples[i].cpu, res_samples[i].tid) < 0) {
while (--i >= 0)
- free(names[i]);
+ zfree(&names[i]);
free(names);
return -1;
}
}
choice = ui__popup_menu(num_res, names);
for (i = 0; i < num_res; i++)
- free(names[i]);
+ zfree(&names[i]);
free(names);
if (choice < 0 || choice >= num_res)
diff --git a/tools/perf/ui/browsers/scripts.c b/tools/perf/ui/browsers/scripts.c
index 27cf3ab88d13..4d565cc14076 100644
--- a/tools/perf/ui/browsers/scripts.c
+++ b/tools/perf/ui/browsers/scripts.c
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
#include "../../util/sort.h"
-#include "../../util/util.h"
#include "../../util/hist.h"
#include "../../util/debug.h"
#include "../../util/symbol.h"
#include "../browser.h"
#include "../libslang.h"
#include "config.h"
+#include <linux/zalloc.h>
#define SCRIPT_NAMELEN 128
#define SCRIPT_MAX_NO 64
@@ -142,7 +142,7 @@ static int list_scripts(char *script_name, bool *custom,
out:
free(buf);
for (i = 0; i < max_std; i++)
- free(paths[i]);
+ zfree(&paths[i]);
return ret;
}
diff --git a/tools/perf/ui/gtk/annotate.c b/tools/perf/ui/gtk/annotate.c
index df49c9ba1785..3af87c18a914 100644
--- a/tools/perf/ui/gtk/annotate.c
+++ b/tools/perf/ui/gtk/annotate.c
@@ -152,7 +152,7 @@ static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym,
gtk_container_add(GTK_CONTAINER(window), view);
list_for_each_entry_safe(pos, n, &notes->src->source, al.node) {
- list_del(&pos->al.node);
+ list_del_init(&pos->al.node);
disasm_line__free(pos);
}
diff --git a/tools/perf/ui/gtk/util.c b/tools/perf/ui/gtk/util.c
index 7250d8101c8f..c28bdb7517ac 100644
--- a/tools/perf/ui/gtk/util.c
+++ b/tools/perf/ui/gtk/util.c
@@ -1,11 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
#include "../util.h"
-#include "../../util/util.h"
#include "../../util/debug.h"
#include "gtk.h"
#include <string.h>
-
+#include <linux/zalloc.h>
struct perf_gtk_context *pgctx;
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
index 89393c79d870..ee7ea6deed21 100644
--- a/tools/perf/ui/stdio/hist.c
+++ b/tools/perf/ui/stdio/hist.c
@@ -3,7 +3,6 @@
#include <linux/string.h>
#include "../../util/callchain.h"
-#include "../../util/util.h"
#include "../../util/hist.h"
#include "../../util/map.h"
#include "../../util/map_groups.h"
@@ -14,6 +13,7 @@
#include "../../util/string2.h"
#include "../../util/thread.h"
#include <linux/ctype.h>
+#include <linux/zalloc.h>
static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
{
diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c
index d4ac41679721..3ad0d3363ac6 100644
--- a/tools/perf/ui/tui/setup.c
+++ b/tools/perf/ui/tui/setup.c
@@ -2,6 +2,7 @@
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
+#include <stdlib.h>
#include <linux/kernel.h>
#ifdef HAVE_BACKTRACE_SUPPORT
#include <execinfo.h>
diff --git a/tools/perf/ui/tui/util.c b/tools/perf/ui/tui/util.c
index b9794d6185af..fe5e571816fc 100644
--- a/tools/perf/ui/tui/util.c
+++ b/tools/perf/ui/tui/util.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
-#include "../../util/util.h"
#include <signal.h>
#include <stdbool.h>
#include <string.h>
+#include <stdlib.h>
#include <sys/ttydefaults.h>
#include "../../util/cache.h"
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index d3408a463060..d7e3b008a613 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -25,6 +25,7 @@ perf-y += rbtree.o
perf-y += libstring.o
perf-y += bitmap.o
perf-y += hweight.o
+perf-y += zalloc.o
perf-y += smt.o
perf-y += strbuf.o
perf-y += string.o
@@ -241,3 +242,7 @@ $(OUTPUT)util/hweight.o: ../lib/hweight.c FORCE
$(OUTPUT)util/vsprintf.o: ../lib/vsprintf.c FORCE
$(call rule_mkdir)
$(call if_changed_dep,cc_o_c)
+
+$(OUTPUT)util/zalloc.o: ../lib/zalloc.c FORCE
+ $(call rule_mkdir)
+ $(call if_changed_dep,cc_o_c)
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index ec7aaf31c2b2..ac9ad2330f93 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -1119,16 +1119,14 @@ static int disasm_line__parse(char *line, const char **namep, char **rawp)
*namep = strdup(name);
if (*namep == NULL)
- goto out_free_name;
+ goto out;
(*rawp)[0] = tmp;
*rawp = skip_spaces(*rawp);
return 0;
-out_free_name:
- free((void *)namep);
- *namep = NULL;
+out:
return -1;
}
@@ -1237,8 +1235,7 @@ void disasm_line__free(struct disasm_line *dl)
dl->ins.ops->free(&dl->ops);
else
ins__delete(&dl->ops);
- free((void *)dl->ins.name);
- dl->ins.name = NULL;
+ zfree(&dl->ins.name);
annotation_line__delete(&dl->al);
}
@@ -1589,7 +1586,7 @@ static void delete_last_nop(struct symbol *sym)
return;
}
- list_del(&dl->al.node);
+ list_del_init(&dl->al.node);
disasm_line__free(dl);
}
}
@@ -2466,7 +2463,7 @@ void annotated_source__purge(struct annotated_source *as)
struct annotation_line *al, *n;
list_for_each_entry_safe(al, n, &as->source, node) {
- list_del(&al->node);
+ list_del_init(&al->node);
disasm_line__free(disasm_line(al));
}
}
diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c
index 6067267cc76c..a314e5b26e9d 100644
--- a/tools/perf/util/arm-spe.c
+++ b/tools/perf/util/arm-spe.c
@@ -12,6 +12,7 @@
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/log2.h>
+#include <linux/zalloc.h>
#include "cpumap.h"
#include "color.h"
@@ -19,7 +20,6 @@
#include "evlist.h"
#include "machine.h"
#include "session.h"
-#include "util.h"
#include "thread.h"
#include "debug.h"
#include "auxtrace.h"
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index bc215fe0b4b4..ec0af36697c4 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -24,9 +24,9 @@
#include <stdlib.h>
#include <stdio.h>
#include <linux/list.h>
+#include <linux/zalloc.h>
#include "../perf.h"
-#include "util.h"
#include "evlist.h"
#include "dso.h"
#include "map.h"
@@ -408,7 +408,7 @@ void auxtrace_queues__free(struct auxtrace_queues *queues)
buffer = list_entry(queues->queue_array[i].head.next,
struct auxtrace_buffer, list);
- list_del(&buffer->list);
+ list_del_init(&buffer->list);
auxtrace_buffer__free(buffer);
}
}
@@ -612,7 +612,7 @@ void auxtrace_index__free(struct list_head *head)
struct auxtrace_index *auxtrace_index, *n;
list_for_each_entry_safe(auxtrace_index, n, head, list) {
- list_del(&auxtrace_index->list);
+ list_del_init(&auxtrace_index->list);
free(auxtrace_index);
}
}
@@ -1413,7 +1413,7 @@ void auxtrace_cache__free(struct auxtrace_cache *c)
return;
auxtrace_cache__drop(c);
- free(c->hashtable);
+ zfree(&c->hashtable);
free(c);
}
@@ -1459,12 +1459,11 @@ void *auxtrace_cache__lookup(struct auxtrace_cache *c, u32 key)
static void addr_filter__free_str(struct addr_filter *filt)
{
- free(filt->str);
+ zfree(&filt->str);
filt->action = NULL;
filt->sym_from = NULL;
filt->sym_to = NULL;
filt->filename = NULL;
- filt->str = NULL;
}
static struct addr_filter *addr_filter__new(void)
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 251d9ea6252f..c61974a50aa5 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -12,6 +12,7 @@
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/string.h>
+#include <linux/zalloc.h>
#include <errno.h>
#include "perf.h"
#include "debug.h"
@@ -828,7 +829,7 @@ static void
bpf_map_op__delete(struct bpf_map_op *op)
{
if (!list_empty(&op->list))
- list_del(&op->list);
+ list_del_init(&op->list);
if (op->key_type == BPF_MAP_KEY_RANGES)
parse_events__clear_array(&op->k.array);
free(op);
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index 89c6913dfc25..f1abfab7aa8c 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -30,6 +30,7 @@
#include "strlist.h"
#include <linux/ctype.h>
+#include <linux/zalloc.h>
static bool no_buildid_cache;
diff --git a/tools/perf/util/call-path.c b/tools/perf/util/call-path.c
index c5b90300304d..5c60b8be1cf6 100644
--- a/tools/perf/util/call-path.c
+++ b/tools/perf/util/call-path.c
@@ -6,8 +6,9 @@
#include <linux/rbtree.h>
#include <linux/list.h>
+#include <linux/zalloc.h>
+#include <stdlib.h>
-#include "util.h"
#include "call-path.h"
static void call_path__init(struct call_path *cp, struct call_path *parent,
@@ -39,7 +40,7 @@ void call_path_root__free(struct call_path_root *cpr)
struct call_path_block *pos, *n;
list_for_each_entry_safe(pos, n, &cpr->blocks, node) {
- list_del(&pos->node);
+ list_del_init(&pos->node);
free(pos);
}
free(cpr);
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index abb608b09269..8d7d8f62fcca 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -16,11 +16,11 @@
#include <stdbool.h>
#include <errno.h>
#include <math.h>
+#include <linux/zalloc.h>
#include "asm/bug.h"
#include "hist.h"
-#include "util.h"
#include "sort.h"
#include "machine.h"
#include "map.h"
@@ -636,7 +636,7 @@ add_child(struct callchain_node *parent,
struct callchain_list *call, *tmp;
list_for_each_entry_safe(call, tmp, &new->val, list) {
- list_del(&call->list);
+ list_del_init(&call->list);
map__zput(call->ms.map);
free(call);
}
@@ -1002,7 +1002,7 @@ merge_chain_branch(struct callchain_cursor *cursor,
callchain_cursor_append(cursor, list->ip,
list->ms.map, list->ms.sym,
false, NULL, 0, 0, 0, list->srcline);
- list_del(&list->list);
+ list_del_init(&list->list);
map__zput(list->ms.map);
free(list);
}
@@ -1453,13 +1453,13 @@ static void free_callchain_node(struct callchain_node *node)
struct rb_node *n;
list_for_each_entry_safe(list, tmp, &node->parent_val, list) {
- list_del(&list->list);
+ list_del_init(&list->list);
map__zput(list->ms.map);
free(list);
}
list_for_each_entry_safe(list, tmp, &node->val, list) {
- list_del(&list->list);
+ list_del_init(&list->list);
map__zput(list->ms.map);
free(list);
}
@@ -1544,7 +1544,7 @@ int callchain_node__make_parent_list(struct callchain_node *node)
out:
list_for_each_entry_safe(chain, new, &head, list) {
- list_del(&chain->list);
+ list_del_init(&chain->list);
map__zput(chain->ms.map);
free(chain);
}
diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c
index ccd02634a616..484c29830a81 100644
--- a/tools/perf/util/cgroup.c
+++ b/tools/perf/util/cgroup.c
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
-#include "util.h"
#include "../perf.h"
#include <subcmd/parse-options.h>
#include "evsel.h"
#include "cgroup.h"
#include "evlist.h"
#include <linux/stringify.h>
+#include <linux/zalloc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -124,7 +124,7 @@ static struct cgroup *cgroup__new(const char *name)
return cgroup;
out_free_name:
- free(cgroup->name);
+ zfree(&cgroup->name);
out_err:
free(cgroup);
return NULL;
diff --git a/tools/perf/util/comm.c b/tools/perf/util/comm.c
index 1066de92af12..afb8d4fd2644 100644
--- a/tools/perf/util/comm.c
+++ b/tools/perf/util/comm.c
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
#include "comm.h"
-#include "util.h"
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <linux/refcount.h>
#include <linux/rbtree.h>
+#include <linux/zalloc.h>
#include "rwsem.h"
struct comm_str {
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index 752cce853e51..042ffbc8c53f 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -11,7 +11,6 @@
*/
#include <errno.h>
#include <sys/param.h>
-#include "util.h"
#include "cache.h"
#include "callchain.h"
#include <subcmd/exec-cmd.h>
@@ -23,7 +22,7 @@
#include <sys/stat.h>
#include <unistd.h>
#include <linux/string.h>
-
+#include <linux/zalloc.h>
#include <linux/ctype.h>
#define MAXNAME (256)
diff --git a/tools/perf/util/counts.c b/tools/perf/util/counts.c
index 03032b410c29..88be9c4365e0 100644
--- a/tools/perf/util/counts.c
+++ b/tools/perf/util/counts.c
@@ -3,7 +3,7 @@
#include <stdlib.h>
#include "evsel.h"
#include "counts.h"
-#include "util.h"
+#include <linux/zalloc.h>
struct perf_counts *perf_counts__new(int ncpus, int nthreads)
{
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 0d8fbedf7bd5..3acfbe34ebaf 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
-#include "util.h"
#include <api/fs/fs.h>
#include "../perf.h"
#include "cpumap.h"
@@ -11,6 +10,7 @@
#include "asm/bug.h"
#include <linux/ctype.h>
+#include <linux/zalloc.h>
static int max_cpu_num;
static int max_present_cpu_num;
diff --git a/tools/perf/util/cputopo.c b/tools/perf/util/cputopo.c
index 26e73a4bd4fe..64336a280967 100644
--- a/tools/perf/util/cputopo.c
+++ b/tools/perf/util/cputopo.c
@@ -2,11 +2,12 @@
#include <sys/param.h>
#include <sys/utsname.h>
#include <inttypes.h>
+#include <stdlib.h>
#include <api/fs/fs.h>
+#include <linux/zalloc.h>
#include "cputopo.h"
#include "cpumap.h"
-#include "util.h"
#include "env.h"
#define CORE_SIB_FMT \
@@ -343,7 +344,7 @@ void numa_topology__delete(struct numa_topology *tp)
u32 i;
for (i = 0; i < tp->nr; i++)
- free(tp->nodes[i].cpus);
+ zfree(&tp->nodes[i].cpus);
free(tp);
}
diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
index bb45e23018ee..37d7c492b155 100644
--- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
+++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
@@ -8,6 +8,7 @@
#include <linux/err.h>
#include <linux/list.h>
+#include <linux/zalloc.h>
#include <stdlib.h>
#include <opencsd/c_api/opencsd_c_api.h>
#include <opencsd/etmv4/trc_pkt_types_etmv4.h>
diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
index 0c7776b51045..67b88b599a53 100644
--- a/tools/perf/util/cs-etm.c
+++ b/tools/perf/util/cs-etm.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/log2.h>
#include <linux/types.h>
+#include <linux/zalloc.h>
#include <opencsd/ocsd_if_types.h>
#include <stdlib.h>
@@ -554,8 +555,7 @@ static void cs_etm__free_traceid_queues(struct cs_etm_queue *etmq)
etmq->traceid_queues_list = NULL;
/* finally free the traceid_queues array */
- free(etmq->traceid_queues);
- etmq->traceid_queues = NULL;
+ zfree(&etmq->traceid_queues);
}
static void cs_etm__free_queue(void *priv)
@@ -2538,7 +2538,7 @@ int cs_etm__process_auxtrace_info(union perf_event *event,
return 0;
}
- if (session->itrace_synth_opts && session->itrace_synth_opts->set) {
+ if (session->itrace_synth_opts->set) {
etm->synth_opts = *session->itrace_synth_opts;
} else {
itrace_synth_opts__set_default(&etm->synth_opts,
@@ -2568,7 +2568,7 @@ err_free_etm:
err_free_metadata:
/* No need to check @metadata[j], free(NULL) is supported */
for (j = 0; j < num_cpu; j++)
- free(metadata[j]);
+ zfree(&metadata[j]);
zfree(&metadata);
err_free_traceid_list:
intlist__delete(traceid_list);
diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c
index 7b06e7373b9e..ddbcd59f2d9b 100644
--- a/tools/perf/util/data-convert-bt.c
+++ b/tools/perf/util/data-convert-bt.c
@@ -10,6 +10,7 @@
#include <inttypes.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
+#include <linux/zalloc.h>
#include <babeltrace/ctf-writer/writer.h>
#include <babeltrace/ctf-writer/clock.h>
#include <babeltrace/ctf-writer/stream.h>
@@ -22,7 +23,6 @@
#include "asm/bug.h"
#include "data-convert-bt.h"
#include "session.h"
-#include "util.h"
#include "debug.h"
#include "tool.h"
#include "evlist.h"
@@ -1353,7 +1353,7 @@ static void free_streams(struct ctf_writer *cw)
for (cpu = 0; cpu < cw->stream_cnt; cpu++)
ctf_stream__delete(cw->stream[cpu]);
- free(cw->stream);
+ zfree(&cw->stream);
}
static int ctf_writer__setup_env(struct ctf_writer *cw,
diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c
index 6a64f713710d..1d1b97a92c3f 100644
--- a/tools/perf/util/data.c
+++ b/tools/perf/util/data.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
#include <linux/kernel.h>
+#include <linux/zalloc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
@@ -20,7 +21,7 @@ static void close_dir(struct perf_data_file *files, int nr)
{
while (--nr >= 1) {
close(files[nr].fd);
- free(files[nr].path);
+ zfree(&files[nr].path);
}
free(files);
}
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index 2182f552aac6..2394c7506abe 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -5,6 +5,7 @@
*/
#include <errno.h>
+#include <stdlib.h>
#include "evsel.h"
#include "machine.h"
@@ -13,11 +14,11 @@
#include "symbol.h"
#include "map.h"
#include "event.h"
-#include "util.h"
#include "thread-stack.h"
#include "callchain.h"
#include "call-path.h"
#include "db-export.h"
+#include <linux/zalloc.h>
struct deferred_export {
struct list_head node;
@@ -33,7 +34,7 @@ static int db_export__deferred(struct db_export *dbe)
de = list_entry(dbe->deferred.next, struct deferred_export,
node);
err = dbe->export_comm(dbe, de->comm);
- list_del(&de->node);
+ list_del_init(&de->node);
free(de);
if (err)
return err;
@@ -49,7 +50,7 @@ static void db_export__free_deferred(struct db_export *dbe)
while (!list_empty(&dbe->deferred)) {
de = list_entry(dbe->deferred.next, struct deferred_export,
node);
- list_del(&de->node);
+ list_del_init(&de->node);
free(de);
}
}
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index 3cc578343f48..3780fe42453b 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -7,6 +7,7 @@
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
+#include <stdlib.h>
#include <sys/wait.h>
#include <api/debug.h>
#include <linux/time64.h>
diff --git a/tools/perf/util/demangle-java.c b/tools/perf/util/demangle-java.c
index 5b4900d67c80..763328c151e9 100644
--- a/tools/perf/util/demangle-java.c
+++ b/tools/perf/util/demangle-java.c
@@ -1,14 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
#include <sys/types.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-#include "util.h"
#include "debug.h"
#include "symbol.h"
#include "demangle-java.h"
#include <linux/ctype.h>
+#include <linux/kernel.h>
enum {
MODE_PREFIX = 0,
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index c7fde04400f7..ebc9d46c15a7 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -2,6 +2,7 @@
#include <asm/bug.h>
#include <linux/kernel.h>
#include <linux/string.h>
+#include <linux/zalloc.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/types.h>
@@ -21,7 +22,7 @@
#include "dso.h"
#include "machine.h"
#include "auxtrace.h"
-#include "util.h"
+#include "util.h" /* O_CLOEXEC for older systems */
#include "debug.h"
#include "string2.h"
#include "vdso.h"
@@ -433,7 +434,7 @@ static void dso__list_add(struct dso *dso)
static void dso__list_del(struct dso *dso)
{
- list_del(&dso->data.open_entry);
+ list_del_init(&dso->data.open_entry);
WARN_ONCE(dso__data_open_cnt <= 0,
"DSO data fd counter out of bounds.");
dso__data_open_cnt--;
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
index 218bfea8f8a8..03b2de1f5a35 100644
--- a/tools/perf/util/dwarf-aux.c
+++ b/tools/perf/util/dwarf-aux.c
@@ -6,7 +6,7 @@
#include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
-#include "util.h"
+#include <stdlib.h>
#include "debug.h"
#include "dwarf-aux.h"
#include "string2.h"
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index 22eee8942527..9909ec40c6d2 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -2,11 +2,12 @@
#include "cpumap.h"
#include "env.h"
#include <linux/ctype.h>
-#include "util.h"
+#include <linux/zalloc.h>
#include "bpf-event.h"
#include <errno.h>
#include <sys/utsname.h>
#include <bpf/libbpf.h>
+#include <stdlib.h>
struct perf_env perf_env;
@@ -186,7 +187,7 @@ void perf_env__exit(struct perf_env *env)
zfree(&env->caches);
for (i = 0; i < env->nr_memory_nodes; i++)
- free(env->memory_nodes[i].set);
+ zfree(&env->memory_nodes[i].set);
zfree(&env->memory_nodes);
}
@@ -286,9 +287,9 @@ int perf_env__nr_cpus_avail(struct perf_env *env)
void cpu_cache_level__free(struct cpu_cache_level *cache)
{
- free(cache->type);
- free(cache->map);
- free(cache->size);
+ zfree(&cache->type);
+ zfree(&cache->map);
+ zfree(&cache->size);
}
/*
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index e1d0c5ba1f92..f1f4848947ce 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -11,6 +11,7 @@
#include <uapi/linux/mman.h> /* To get things like MAP_HUGETLB even on older libc headers */
#include <api/fs/fs.h>
#include <linux/perf_event.h>
+#include <linux/zalloc.h>
#include "event.h"
#include "debug.h"
#include "hist.h"
@@ -855,7 +856,7 @@ free_threads:
free(synthesize_threads);
free_dirent:
for (i = 0; i < n; i++)
- free(dirent[i]);
+ zfree(&dirent[i]);
free(dirent);
return err;
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index a474ede17cd6..b0364d923f76 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -5,7 +5,6 @@
* Parts came from builtin-{top,stat,record}.c, see those files for further
* copyright notes.
*/
-#include "util.h"
#include <api/fs/fs.h>
#include <errno.h>
#include <inttypes.h>
@@ -33,6 +32,7 @@
#include <linux/hash.h>
#include <linux/log2.h>
#include <linux/err.h>
+#include <linux/zalloc.h>
#ifdef LACKS_SIGQUEUE_PROTOTYPE
int sigqueue(pid_t pid, int sig, const union sigval value);
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 7fb4ae82f34c..ebb46da4dfe5 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -17,6 +17,7 @@
#include <linux/perf_event.h>
#include <linux/compiler.h>
#include <linux/err.h>
+#include <linux/zalloc.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/types.h>
@@ -27,7 +28,6 @@
#include "event.h"
#include "evsel.h"
#include "evlist.h"
-#include "util.h"
#include "cpumap.h"
#include "thread_map.h"
#include "target.h"
@@ -1298,7 +1298,7 @@ static void perf_evsel__free_config_terms(struct perf_evsel *evsel)
struct perf_evsel_config_term *term, *h;
list_for_each_entry_safe(term, h, &evsel->config_terms, list) {
- list_del(&term->list);
+ list_del_init(&term->list);
free(term);
}
}
diff --git a/tools/perf/util/get_current_dir_name.c b/tools/perf/util/get_current_dir_name.c
index 267aa609a582..01f32f26552d 100644
--- a/tools/perf/util/get_current_dir_name.c
+++ b/tools/perf/util/get_current_dir_name.c
@@ -1,8 +1,8 @@
-// SPDX-License-Identifier: GPL-2.0
-// Copyright (C) 2018, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
+// SPDX-License-Identifier: LGPL-2.1
+// Copyright (C) 2018, 2019 Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
//
#ifndef HAVE_GET_CURRENT_DIR_NAME
-#include "util.h"
+#include "get_current_dir_name.h"
#include <unistd.h>
#include <stdlib.h>
#include <stdlib.h>
diff --git a/tools/perf/util/get_current_dir_name.h b/tools/perf/util/get_current_dir_name.h
new file mode 100644
index 000000000000..69f7d5537d32
--- /dev/null
+++ b/tools/perf/util/get_current_dir_name.h
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: LGPL-2.1
+// Copyright (C) 2018, 2019 Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
+//
+#ifndef __PERF_GET_CURRENT_DIR_NAME_H
+#ifndef HAVE_GET_CURRENT_DIR_NAME
+char *get_current_dir_name(void);
+#endif // HAVE_GET_CURRENT_DIR_NAME
+#endif // __PERF_GET_CURRENT_DIR_NAME_H
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 6a93ff5d8db5..c24db7f4909c 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include <inttypes.h>
-#include "util.h"
#include "string2.h"
#include <sys/param.h>
#include <sys/types.h>
@@ -15,6 +14,7 @@
#include <linux/bitops.h>
#include <linux/string.h>
#include <linux/stringify.h>
+#include <linux/zalloc.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <linux/time64.h>
@@ -1052,7 +1052,7 @@ static int cpu_cache_level__read(struct cpu_cache_level *cache, u32 cpu, u16 lev
scnprintf(file, PATH_MAX, "%s/size", path);
if (sysfs__read_str(file, &cache->size, &len)) {
- free(cache->type);
+ zfree(&cache->type);
return -1;
}
@@ -1061,8 +1061,8 @@ static int cpu_cache_level__read(struct cpu_cache_level *cache, u32 cpu, u16 lev
scnprintf(file, PATH_MAX, "%s/shared_cpu_list", path);
if (sysfs__read_str(file, &cache->map, &len)) {
- free(cache->map);
- free(cache->type);
+ zfree(&cache->map);
+ zfree(&cache->type);
return -1;
}
diff --git a/tools/perf/util/help-unknown-cmd.c b/tools/perf/util/help-unknown-cmd.c
index 4f07a5ba5030..ab9e16123626 100644
--- a/tools/perf/util/help-unknown-cmd.c
+++ b/tools/perf/util/help-unknown-cmd.c
@@ -3,9 +3,11 @@
#include "config.h"
#include <poll.h>
#include <stdio.h>
+#include <stdlib.h>
#include <subcmd/help.h>
#include "../builtin.h"
#include "levenshtein.h"
+#include <linux/zalloc.h>
static int autocorrect;
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 27cecb59f866..f24fd1954f6c 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
#include "callchain.h"
-#include "util.h"
#include "build-id.h"
#include "hist.h"
#include "map.h"
@@ -20,6 +19,7 @@
#include <inttypes.h>
#include <sys/param.h>
#include <linux/time64.h>
+#include <linux/zalloc.h>
static bool hists__filter_entry_by_dso(struct hists *hists,
struct hist_entry *he);
@@ -472,16 +472,16 @@ static int hist_entry__init(struct hist_entry *he,
return 0;
err_srcline:
- free(he->srcline);
+ zfree(&he->srcline);
err_rawdata:
- free(he->raw_data);
+ zfree(&he->raw_data);
err_infos:
if (he->branch_info) {
map__put(he->branch_info->from.map);
map__put(he->branch_info->to.map);
- free(he->branch_info);
+ zfree(&he->branch_info);
}
if (he->mem_info) {
map__put(he->mem_info->iaddr.map);
@@ -489,7 +489,7 @@ err_infos:
}
err:
map__zput(he->ms.map);
- free(he->stat_acc);
+ zfree(&he->stat_acc);
return -ENOMEM;
}
@@ -1254,10 +1254,10 @@ void hist_entry__delete(struct hist_entry *he)
zfree(&he->stat_acc);
free_srcline(he->srcline);
if (he->srcfile && he->srcfile[0])
- free(he->srcfile);
+ zfree(&he->srcfile);
free_callchain(he->callchain);
- free(he->trace_output);
- free(he->raw_data);
+ zfree(&he->trace_output);
+ zfree(&he->raw_data);
ops->free(he);
}
@@ -2741,10 +2741,10 @@ static void hists_evsel__exit(struct perf_evsel *evsel)
list_for_each_entry_safe(node, tmp, &hists->hpp_formats, list) {
perf_hpp_list__for_each_format_safe(&node->hpp, fmt, pos) {
- list_del(&fmt->list);
+ list_del_init(&fmt->list);
free(fmt);
}
- list_del(&node->list);
+ list_del_init(&node->list);
free(node);
}
}
diff --git a/tools/perf/util/intel-bts.c b/tools/perf/util/intel-bts.c
index e32dbffebb2f..5560e95afdda 100644
--- a/tools/perf/util/intel-bts.c
+++ b/tools/perf/util/intel-bts.c
@@ -12,6 +12,7 @@
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/log2.h>
+#include <linux/zalloc.h>
#include "cpumap.h"
#include "color.h"
@@ -21,7 +22,6 @@
#include "map.h"
#include "symbol.h"
#include "session.h"
-#include "util.h"
#include "thread.h"
#include "thread-stack.h"
#include "debug.h"
@@ -891,13 +891,12 @@ int intel_bts_process_auxtrace_info(union perf_event *event,
if (dump_trace)
return 0;
- if (session->itrace_synth_opts && session->itrace_synth_opts->set) {
+ if (session->itrace_synth_opts->set) {
bts->synth_opts = *session->itrace_synth_opts;
} else {
itrace_synth_opts__set_default(&bts->synth_opts,
session->itrace_synth_opts->default_no_sample);
- if (session->itrace_synth_opts)
- bts->synth_opts.thread_stack =
+ bts->synth_opts.thread_stack =
session->itrace_synth_opts->thread_stack;
}
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
index 4d14e78c5927..3bfdf2b7a96a 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
@@ -14,9 +14,9 @@
#include <stdint.h>
#include <inttypes.h>
#include <linux/compiler.h>
+#include <linux/zalloc.h>
#include "../cache.h"
-#include "../util.h"
#include "../auxtrace.h"
#include "intel-pt-insn-decoder.h"
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c
index 470aaae9d930..df061599fef4 100644
--- a/tools/perf/util/intel-pt.c
+++ b/tools/perf/util/intel-pt.c
@@ -10,6 +10,7 @@
#include <errno.h>
#include <linux/kernel.h>
#include <linux/types.h>
+#include <linux/zalloc.h>
#include "../perf.h"
#include "session.h"
@@ -22,7 +23,6 @@
#include "evsel.h"
#include "map.h"
#include "color.h"
-#include "util.h"
#include "thread.h"
#include "thread-stack.h"
#include "symbol.h"
@@ -3210,7 +3210,7 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
goto err_delete_thread;
}
- if (session->itrace_synth_opts && session->itrace_synth_opts->set) {
+ if (session->itrace_synth_opts->set) {
pt->synth_opts = *session->itrace_synth_opts;
} else {
itrace_synth_opts__set_default(&pt->synth_opts,
@@ -3220,8 +3220,7 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
pt->synth_opts.branches = false;
pt->synth_opts.callchain = true;
}
- if (session->itrace_synth_opts)
- pt->synth_opts.thread_stack =
+ pt->synth_opts.thread_stack =
session->itrace_synth_opts->thread_stack;
}
@@ -3241,11 +3240,9 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
pt->cbr2khz = tsc_freq / pt->max_non_turbo_ratio / 1000;
}
- if (session->itrace_synth_opts) {
- err = intel_pt_setup_time_ranges(pt, session->itrace_synth_opts);
- if (err)
- goto err_delete_thread;
- }
+ err = intel_pt_setup_time_ranges(pt, session->itrace_synth_opts);
+ if (err)
+ goto err_delete_thread;
if (pt->synth_opts.calls)
pt->branches_filter |= PERF_IP_FLAG_CALL | PERF_IP_FLAG_ASYNC |
diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c
index 28908afedec4..18c34f0c1966 100644
--- a/tools/perf/util/jitdump.c
+++ b/tools/perf/util/jitdump.c
@@ -29,6 +29,7 @@
#include "../builtin.h"
#include <linux/ctype.h>
+#include <linux/zalloc.h>
struct jit_buf_desc {
struct perf_data *output;
@@ -431,14 +432,12 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
jd->unwinding_data, jd->eh_frame_hdr_size, jd->unwinding_size);
if (jd->debug_data && jd->nr_debug_entries) {
- free(jd->debug_data);
- jd->debug_data = NULL;
+ zfree(&jd->debug_data);
jd->nr_debug_entries = 0;
}
if (jd->unwinding_data && jd->eh_frame_hdr_size) {
- free(jd->unwinding_data);
- jd->unwinding_data = NULL;
+ zfree(&jd->unwinding_data);
jd->eh_frame_hdr_size = 0;
jd->unwinding_mapped_size = 0;
jd->unwinding_size = 0;
diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c
index 5b0b60f00275..9f0470ecbca9 100644
--- a/tools/perf/util/llvm-utils.c
+++ b/tools/perf/util/llvm-utils.c
@@ -9,6 +9,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <linux/err.h>
+#include <linux/zalloc.h>
#include "debug.h"
#include "llvm-utils.h"
#include "config.h"
@@ -352,8 +353,7 @@ void llvm__get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts)
" \toption in [llvm] to \"\" to suppress this detection.\n\n",
*kbuild_dir);
- free(*kbuild_dir);
- *kbuild_dir = NULL;
+ zfree(kbuild_dir);
goto errout;
}
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 147ed85ea2bc..cf826eca3aaf 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -15,7 +15,6 @@
#include "strlist.h"
#include "thread.h"
#include "vdso.h"
-#include "util.h"
#include <stdbool.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -28,6 +27,7 @@
#include <linux/ctype.h>
#include <symbol/kallsyms.h>
#include <linux/mman.h>
+#include <linux/zalloc.h>
static void __machine__remove_thread(struct machine *machine, struct thread *th, bool lock);
@@ -810,7 +810,7 @@ struct map *machine__findnew_module_map(struct machine *machine, u64 start,
out:
/* put the dso here, corresponding to machine__findnew_module_dso */
dso__put(dso);
- free(m.name);
+ zfree(&m.name);
return map;
}
@@ -1350,7 +1350,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *mg,
if (m.kmod)
ret = map_groups__set_module_path(mg, path, &m);
- free(m.name);
+ zfree(&m.name);
if (ret)
goto out;
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 6fce983c6115..668410b1d426 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -12,10 +12,10 @@
#include "thread.h"
#include "vdso.h"
#include "build-id.h"
-#include "util.h"
#include "debug.h"
#include "machine.h"
#include <linux/string.h>
+#include <linux/zalloc.h>
#include "srcline.h"
#include "namespaces.h"
#include "unwind.h"
@@ -476,8 +476,11 @@ int map__fprintf_srccode(struct map *map, u64 addr,
goto out_free_line;
ret = fprintf(fp, "|%-8d %.*s", line, len, srccode);
- state->srcfile = srcfile;
- state->line = line;
+
+ if (state) {
+ state->srcfile = srcfile;
+ state->line = line;
+ }
return ret;
out_free_line:
diff --git a/tools/perf/util/mem2node.c b/tools/perf/util/mem2node.c
index c6fd81c02586..cacc2fc4dcbd 100644
--- a/tools/perf/util/mem2node.c
+++ b/tools/perf/util/mem2node.c
@@ -1,8 +1,8 @@
#include <errno.h>
#include <inttypes.h>
#include <linux/bitmap.h>
+#include <linux/zalloc.h>
#include "mem2node.h"
-#include "util.h"
struct phys_entry {
struct rb_node rb_node;
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index d8164574cb16..416a9015405e 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -18,6 +18,7 @@
#include "strlist.h"
#include <assert.h>
#include <linux/ctype.h>
+#include <linux/zalloc.h>
struct metric_event *metricgroup__lookup(struct rblist *metric_events,
struct perf_evsel *evsel,
@@ -235,7 +236,7 @@ static struct rb_node *mep_new(struct rblist *rl __maybe_unused,
goto out_name;
return &me->nd;
out_name:
- free((char *)me->name);
+ zfree(&me->name);
out_me:
free(me);
return NULL;
@@ -263,7 +264,7 @@ static void mep_delete(struct rblist *rl __maybe_unused,
struct mep *me = container_of(nd, struct mep, nd);
strlist__delete(me->metrics);
- free((void *)me->name);
+ zfree(&me->name);
free(me);
}
@@ -489,8 +490,9 @@ static void metricgroup__free_egroups(struct list_head *group_list)
list_for_each_entry_safe (eg, egtmp, group_list, nd) {
for (i = 0; i < eg->idnum; i++)
- free((char *)eg->ids[i]);
- free(eg->ids);
+ zfree(&eg->ids[i]);
+ zfree(&eg->ids);
+ list_del_init(&eg->nd);
free(eg);
}
}
diff --git a/tools/perf/util/mmap.c b/tools/perf/util/mmap.c
index 768c632b0d82..9f0b6391af33 100644
--- a/tools/perf/util/mmap.c
+++ b/tools/perf/util/mmap.c
@@ -9,6 +9,7 @@
#include <sys/mman.h>
#include <inttypes.h>
#include <asm/bug.h>
+#include <linux/zalloc.h>
#ifdef HAVE_LIBNUMA_SUPPORT
#include <numaif.h>
#endif
diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
index 023c4efd788d..46d3a7754897 100644
--- a/tools/perf/util/namespaces.c
+++ b/tools/perf/util/namespaces.c
@@ -5,8 +5,8 @@
*/
#include "namespaces.h"
-#include "util.h"
#include "event.h"
+#include "get_current_dir_name.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -17,6 +17,7 @@
#include <string.h>
#include <unistd.h>
#include <asm/bug.h>
+#include <linux/zalloc.h>
struct namespaces *namespaces__new(struct namespaces_event *event)
{
diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
index 15a5a276c478..004430c0de93 100644
--- a/tools/perf/util/namespaces.h
+++ b/tools/perf/util/namespaces.h
@@ -13,6 +13,10 @@
#include <linux/refcount.h>
#include <linux/types.h>
+#ifndef HAVE_SETNS_SUPPORT
+int setns(int fd, int nstype);
+#endif
+
struct namespaces_event;
struct namespaces {
diff --git a/tools/perf/util/ordered-events.c b/tools/perf/util/ordered-events.c
index 989fed6f43b5..bb5f34b7ab44 100644
--- a/tools/perf/util/ordered-events.c
+++ b/tools/perf/util/ordered-events.c
@@ -138,7 +138,7 @@ static struct ordered_event *alloc_event(struct ordered_events *oe,
if (!list_empty(cache)) {
new = list_entry(cache->next, struct ordered_event, list);
- list_del(&new->list);
+ list_del_init(&new->list);
} else if (oe->buffer) {
new = &oe->buffer->event[oe->buffer_idx];
if (++oe->buffer_idx == MAX_SAMPLE_BUFFER)
@@ -394,13 +394,13 @@ void ordered_events__free(struct ordered_events *oe)
* yet, we need to free only allocated ones ...
*/
if (oe->buffer) {
- list_del(&oe->buffer->list);
+ list_del_init(&oe->buffer->list);
ordered_events_buffer__free(oe->buffer, oe->buffer_idx, oe);
}
/* ... and continue with the rest */
list_for_each_entry_safe(buffer, tmp, &oe->to_free, list) {
- list_del(&buffer->list);
+ list_del_init(&buffer->list);
ordered_events_buffer__free(buffer, MAX_SAMPLE_BUFFER, oe);
}
}
diff --git a/tools/perf/util/parse-branch-options.c b/tools/perf/util/parse-branch-options.c
index bd779d9f4d1e..726e8d9e8c54 100644
--- a/tools/perf/util/parse-branch-options.c
+++ b/tools/perf/util/parse-branch-options.c
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
#include "perf.h"
-#include "util/util.h"
#include "util/debug.h"
#include <subcmd/parse-options.h>
#include "util/parse-branch-options.h"
+#include <stdlib.h>
#define BRANCH_OPT(n, m) \
{ .name = n, .mode = (m) }
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index cf0b9b81c5aa..371ff3aee769 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/hw_breakpoint.h>
#include <linux/err.h>
+#include <linux/zalloc.h>
#include <dirent.h>
#include <errno.h>
#include <sys/ioctl.h>
@@ -651,7 +652,7 @@ static int add_bpf_event(const char *group, const char *event, int fd,
pr_debug("Failed to add BPF event %s:%s\n",
group, event);
list_for_each_entry_safe(evsel, tmp, &new_evsels, node) {
- list_del(&evsel->node);
+ list_del_init(&evsel->node);
perf_evsel__delete(evsel);
}
return err;
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 6ad8d4914969..f1c36ed1cf36 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -480,7 +480,6 @@ event_bpf_file:
PE_BPF_OBJECT opt_event_config
{
struct parse_events_state *parse_state = _parse_state;
- struct parse_events_error *error = parse_state->error;
struct list_head *list;
ALLOC_LIST(list);
@@ -626,7 +625,6 @@ PE_TERM
PE_NAME array '=' PE_NAME
{
struct parse_events_term *term;
- int i;
ABORT_ON(parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER,
$1, $4, &@1, &@4));
diff --git a/tools/perf/util/parse-regs-options.c b/tools/perf/util/parse-regs-options.c
index 08581e276225..ef46c2848808 100644
--- a/tools/perf/util/parse-regs-options.c
+++ b/tools/perf/util/parse-regs-options.c
@@ -1,8 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
-#include "perf.h"
-#include "util/util.h"
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
#include "util/debug.h"
#include <subcmd/parse-options.h>
+#include "util/perf_regs.h"
#include "util/parse-regs-options.h"
static int
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 55f4de6442e3..f32b710347db 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -2,6 +2,7 @@
#include <linux/list.h>
#include <linux/compiler.h>
#include <linux/string.h>
+#include <linux/zalloc.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
@@ -14,7 +15,6 @@
#include <api/fs/fs.h>
#include <locale.h>
#include <regex.h>
-#include "util.h"
#include "pmu.h"
#include "parse-events.h"
#include "cpumap.h"
@@ -1245,7 +1245,7 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
info->metric_expr = alias->metric_expr;
info->metric_name = alias->metric_name;
- list_del(&term->list);
+ list_del_init(&term->list);
free(term);
}
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 6f24eaf6e504..0c3b55d0617d 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -19,7 +19,6 @@
#include <limits.h>
#include <elf.h>
-#include "util.h"
#include "event.h"
#include "namespaces.h"
#include "strlist.h"
@@ -40,6 +39,7 @@
#include "string2.h"
#include <linux/ctype.h>
+#include <linux/zalloc.h>
#define PERFPROBE_GROUP "probe"
@@ -214,9 +214,9 @@ out:
static void clear_perf_probe_point(struct perf_probe_point *pp)
{
- free(pp->file);
- free(pp->function);
- free(pp->lazy_line);
+ zfree(&pp->file);
+ zfree(&pp->function);
+ zfree(&pp->lazy_line);
}
static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
@@ -1175,12 +1175,11 @@ int show_available_vars(struct perf_probe_event *pevs __maybe_unused,
void line_range__clear(struct line_range *lr)
{
- free(lr->function);
- free(lr->file);
- free(lr->path);
- free(lr->comp_dir);
+ zfree(&lr->function);
+ zfree(&lr->file);
+ zfree(&lr->path);
+ zfree(&lr->comp_dir);
intlist__delete(lr->line_list);
- memset(lr, 0, sizeof(*lr));
}
int line_range__init(struct line_range *lr)
@@ -2203,15 +2202,15 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
struct perf_probe_arg_field *field, *next;
int i;
- free(pev->event);
- free(pev->group);
- free(pev->target);
+ zfree(&pev->event);
+ zfree(&pev->group);
+ zfree(&pev->target);
clear_perf_probe_point(&pev->point);
for (i = 0; i < pev->nargs; i++) {
- free(pev->args[i].name);
- free(pev->args[i].var);
- free(pev->args[i].type);
+ zfree(&pev->args[i].name);
+ zfree(&pev->args[i].var);
+ zfree(&pev->args[i].type);
field = pev->args[i].field;
while (field) {
next = field->next;
@@ -2220,8 +2219,7 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
field = next;
}
}
- free(pev->args);
- memset(pev, 0, sizeof(*pev));
+ zfree(&pev->args);
}
#define strdup_or_goto(str, label) \
@@ -2302,15 +2300,15 @@ void clear_probe_trace_event(struct probe_trace_event *tev)
struct probe_trace_arg_ref *ref, *next;
int i;
- free(tev->event);
- free(tev->group);
- free(tev->point.symbol);
- free(tev->point.realname);
- free(tev->point.module);
+ zfree(&tev->event);
+ zfree(&tev->group);
+ zfree(&tev->point.symbol);
+ zfree(&tev->point.realname);
+ zfree(&tev->point.module);
for (i = 0; i < tev->nargs; i++) {
- free(tev->args[i].name);
- free(tev->args[i].value);
- free(tev->args[i].type);
+ zfree(&tev->args[i].name);
+ zfree(&tev->args[i].value);
+ zfree(&tev->args[i].type);
ref = tev->args[i].ref;
while (ref) {
next = ref->next;
@@ -2318,8 +2316,7 @@ void clear_probe_trace_event(struct probe_trace_event *tev)
ref = next;
}
}
- free(tev->args);
- memset(tev, 0, sizeof(*tev));
+ zfree(&tev->args);
}
struct kprobe_blacklist_node {
@@ -2336,8 +2333,8 @@ static void kprobe_blacklist__delete(struct list_head *blacklist)
while (!list_empty(blacklist)) {
node = list_first_entry(blacklist,
struct kprobe_blacklist_node, list);
- list_del(&node->list);
- free(node->symbol);
+ list_del_init(&node->list);
+ zfree(&node->symbol);
free(node);
}
}
diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c
index 0ed1900454eb..c2998f90b23c 100644
--- a/tools/perf/util/probe-file.c
+++ b/tools/perf/util/probe-file.c
@@ -10,8 +10,8 @@
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
+#include <linux/zalloc.h>
#include "namespaces.h"
-#include "util.h"
#include "event.h"
#include "strlist.h"
#include "strfilter.h"
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 6b40cc691a2d..7d8c99734928 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -19,11 +19,11 @@
#include <dwarf-regs.h>
#include <linux/bitops.h>
+#include <linux/zalloc.h>
#include "event.h"
#include "dso.h"
#include "debug.h"
#include "intlist.h"
-#include "util.h"
#include "strlist.h"
#include "symbol.h"
#include "probe-finder.h"
diff --git a/tools/perf/util/pstack.c b/tools/perf/util/pstack.c
index 797fe1ae2d2e..28de8a4c2ce8 100644
--- a/tools/perf/util/pstack.c
+++ b/tools/perf/util/pstack.c
@@ -5,10 +5,10 @@
* (c) 2010 Arnaldo Carvalho de Melo <acme@redhat.com>
*/
-#include "util.h"
#include "pstack.h"
#include "debug.h"
#include <linux/kernel.h>
+#include <linux/zalloc.h>
#include <stdlib.h>
struct pstack {
diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources
index 2237bac9fadb..ceb8afdf9a89 100644
--- a/tools/perf/util/python-ext-sources
+++ b/tools/perf/util/python-ext-sources
@@ -18,6 +18,7 @@ util/namespaces.c
../lib/hweight.c
../lib/string.c
../lib/vsprintf.c
+../lib/zalloc.c
util/thread_map.c
util/util.c
util/xyarray.c
diff --git a/tools/perf/util/s390-cpumsf.c b/tools/perf/util/s390-cpumsf.c
index 10d36d9b7909..83d2e149ef19 100644
--- a/tools/perf/util/s390-cpumsf.c
+++ b/tools/perf/util/s390-cpumsf.c
@@ -146,6 +146,7 @@
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/log2.h>
+#include <linux/zalloc.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -156,7 +157,6 @@
#include "evlist.h"
#include "machine.h"
#include "session.h"
-#include "util.h"
#include "thread.h"
#include "debug.h"
#include "auxtrace.h"
@@ -756,7 +756,7 @@ static int s390_cpumsf_run_decoder(struct s390_cpumsf_queue *sfq,
*/
if (err) {
sfq->buffer = NULL;
- list_del(&buffer->list);
+ list_del_init(&buffer->list);
auxtrace_buffer__free(buffer);
if (err > 0) /* Buffer done, no error */
err = 0;
@@ -1044,7 +1044,7 @@ static void s390_cpumsf_free(struct perf_session *session)
auxtrace_heap__free(&sf->heap);
s390_cpumsf_free_queues(session);
session->auxtrace = NULL;
- free(sf->logdir);
+ zfree(&sf->logdir);
free(sf);
}
@@ -1101,8 +1101,7 @@ static int s390_cpumsf__config(const char *var, const char *value, void *cb)
if (rc == -1 || !S_ISDIR(stbuf.st_mode)) {
pr_err("Missing auxtrace log directory %s,"
" continue with current directory...\n", value);
- free(sf->logdir);
- sf->logdir = NULL;
+ zfree(&sf->logdir);
}
return 1;
}
@@ -1162,7 +1161,7 @@ err_free_queues:
auxtrace_queues__free(&sf->queues);
session->auxtrace = NULL;
err_free:
- free(sf->logdir);
+ zfree(&sf->logdir);
free(sf);
return err;
}
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 54cf163347f7..d0fd6c614e68 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -2,6 +2,7 @@
#include <errno.h>
#include <inttypes.h>
#include <linux/kernel.h>
+#include <linux/zalloc.h>
#include <traceevent/event-parse.h>
#include <api/fs/fs.h>
@@ -18,7 +19,6 @@
#include "session.h"
#include "tool.h"
#include "sort.h"
-#include "util.h"
#include "cpumap.h"
#include "perf_regs.h"
#include "asm/bug.h"
@@ -1246,9 +1246,12 @@ static void dump_read(struct perf_evsel *evsel, union perf_event *event)
return;
printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid,
- evsel ? perf_evsel__name(evsel) : "FAIL",
+ perf_evsel__name(evsel),
event->read.value);
+ if (!evsel)
+ return;
+
read_format = evsel->attr.read_format;
if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
diff --git a/tools/perf/util/setns.c b/tools/perf/util/setns.c
index ce8fc290fce8..48f9c0af63b2 100644
--- a/tools/perf/util/setns.c
+++ b/tools/perf/util/setns.c
@@ -1,4 +1,6 @@
-#include "util.h"
+// SPDX-License-Identifier: LGPL-2.1
+
+#include "namespaces.h"
#include <unistd.h>
#include <sys/syscall.h>
diff --git a/tools/perf/util/srccode.c b/tools/perf/util/srccode.c
index 684b155c222a..adfcf1ff464c 100644
--- a/tools/perf/util/srccode.c
+++ b/tools/perf/util/srccode.c
@@ -4,7 +4,8 @@
* Copyright (c) 2017, Intel Corporation.
* Author: Andi Kleen
*/
-#include "linux/list.h"
+#include <linux/list.h>
+#include <linux/zalloc.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
@@ -82,12 +83,12 @@ static void fill_lines(char **lines, int maxline, char *map, int maplen)
static void free_srcfile(struct srcfile *sf)
{
- list_del(&sf->nd);
+ list_del_init(&sf->nd);
hlist_del(&sf->hash_nd);
map_total_sz -= sf->maplen;
munmap(sf->map, sf->maplen);
- free(sf->lines);
- free(sf->fn);
+ zfree(&sf->lines);
+ zfree(&sf->fn);
free(sf);
num_srcfiles--;
}
@@ -153,7 +154,7 @@ static struct srcfile *find_srcfile(char *fn)
out_map:
munmap(h->map, sz);
out_fn:
- free(h->fn);
+ zfree(&h->fn);
out_h:
free(h);
return NULL;
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c
index dcad75daf5e4..6ccf6f6d09df 100644
--- a/tools/perf/util/srcline.c
+++ b/tools/perf/util/srcline.c
@@ -6,9 +6,9 @@
#include <linux/kernel.h>
#include <linux/string.h>
+#include <linux/zalloc.h>
#include "util/dso.h"
-#include "util/util.h"
#include "util/debug.h"
#include "util/callchain.h"
#include "util/symbol_conf.h"
diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c
index cb891e5c2969..656065af4971 100644
--- a/tools/perf/util/stat-shadow.c
+++ b/tools/perf/util/stat-shadow.c
@@ -8,6 +8,7 @@
#include "evlist.h"
#include "expr.h"
#include "metricgroup.h"
+#include <linux/zalloc.h>
/*
* AGGR_GLOBAL: Use CPU 0
@@ -775,7 +776,7 @@ static void generic_metric(struct perf_stat_config *config,
print_metric(config, ctxp, NULL, NULL, "", 0);
for (i = 1; i < pctx.num_ids; i++)
- free((void *)pctx.ids[i].name);
+ zfree(&pctx.ids[i].name);
}
void perf_stat__print_shadow_stats(struct perf_stat_config *config,
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c
index d91fe754b6d2..db8a6cf336be 100644
--- a/tools/perf/util/stat.c
+++ b/tools/perf/util/stat.c
@@ -6,6 +6,7 @@
#include "evlist.h"
#include "evsel.h"
#include "thread_map.h"
+#include <linux/zalloc.h>
void update_stats(struct stats *stats, u64 val)
{
@@ -132,7 +133,7 @@ static void perf_evsel__free_stat_priv(struct perf_evsel *evsel)
struct perf_stat_evsel *ps = evsel->stats;
if (ps)
- free(ps->group_data);
+ zfree(&ps->group_data);
zfree(&evsel->stats);
}
diff --git a/tools/perf/util/strbuf.c b/tools/perf/util/strbuf.c
index 23092fd6451d..2ce0dc887364 100644
--- a/tools/perf/util/strbuf.c
+++ b/tools/perf/util/strbuf.c
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
#include "debug.h"
-#include "util.h"
#include <linux/kernel.h>
+#include <linux/zalloc.h>
#include <errno.h>
+#include <stdlib.h>
/*
* Used as the default ->buf value, so that people can always assume
diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c
index 90ea2b209cbb..78aa4c3b990d 100644
--- a/tools/perf/util/strfilter.c
+++ b/tools/perf/util/strfilter.c
@@ -1,11 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
-#include "util.h"
#include "string2.h"
#include "strfilter.h"
#include <errno.h>
+#include <stdlib.h>
#include <linux/ctype.h>
#include <linux/string.h>
+#include <linux/zalloc.h>
/* Operators */
static const char *OP_and = "&"; /* Logical AND */
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c
index af45c6fd97db..8a868cbeffae 100644
--- a/tools/perf/util/strlist.c
+++ b/tools/perf/util/strlist.c
@@ -4,12 +4,12 @@
*/
#include "strlist.h"
-#include "util.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <linux/zalloc.h>
static
struct rb_node *strlist__node_new(struct rblist *rblist, const void *entry)
diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c
index fab8a048d31b..76cc54000483 100644
--- a/tools/perf/util/svghelper.c
+++ b/tools/perf/util/svghelper.c
@@ -15,10 +15,10 @@
#include <string.h>
#include <linux/bitmap.h>
#include <linux/time64.h>
+#include <linux/zalloc.h>
#include "perf.h"
#include "svghelper.h"
-#include "util.h"
#include "cpumap.h"
static u64 first_time, last_time;
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 62008756d8cc..7d504dc22108 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -2,6 +2,7 @@
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
+#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <inttypes.h>
@@ -16,6 +17,7 @@
#include "debug.h"
#include "util.h"
#include <linux/ctype.h>
+#include <linux/zalloc.h>
#include <symbol/kallsyms.h>
#ifndef EM_AARCH64
@@ -1476,7 +1478,7 @@ static void kcore_copy__free_phdrs(struct kcore_copy_info *kci)
struct phdr_data *p, *tmp;
list_for_each_entry_safe(p, tmp, &kci->phdrs, node) {
- list_del(&p->node);
+ list_del_init(&p->node);
free(p);
}
}
@@ -1499,7 +1501,7 @@ static void kcore_copy__free_syms(struct kcore_copy_info *kci)
struct sym_data *s, *tmp;
list_for_each_entry_safe(s, tmp, &kci->syms, node) {
- list_del(&s->node);
+ list_del_init(&s->node);
free(s);
}
}
@@ -2131,11 +2133,11 @@ static int populate_sdt_note(Elf **elf, const char *data, size_t len,
return 0;
out_free_args:
- free(tmp->args);
+ zfree(&tmp->args);
out_free_name:
- free(tmp->name);
+ zfree(&tmp->name);
out_free_prov:
- free(tmp->provider);
+ zfree(&tmp->provider);
out_free_note:
free(tmp);
out_err:
@@ -2250,9 +2252,9 @@ int cleanup_sdt_note_list(struct list_head *sdt_notes)
int nr_free = 0;
list_for_each_entry_safe(pos, tmp, sdt_notes, note_list) {
- list_del(&pos->note_list);
- free(pos->name);
- free(pos->provider);
+ list_del_init(&pos->note_list);
+ zfree(&pos->name);
+ zfree(&pos->provider);
free(pos);
nr_free++;
}
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c
index 17edbd4f6f85..3bc8b7e3300e 100644
--- a/tools/perf/util/symbol-minimal.c
+++ b/tools/perf/util/symbol-minimal.c
@@ -7,9 +7,10 @@
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
+#include <stdlib.h>
#include <byteswap.h>
#include <sys/stat.h>
-
+#include <linux/zalloc.h>
static bool check_need_swap(int file_endian)
{
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index ae2ce255e848..173f3378aaa0 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -26,6 +26,7 @@
#include "header.h"
#include "path.h"
#include <linux/ctype.h>
+#include <linux/zalloc.h>
#include <elf.h>
#include <limits.h>
diff --git a/tools/perf/util/syscalltbl.c b/tools/perf/util/syscalltbl.c
index c2037ac533f3..022a9c670338 100644
--- a/tools/perf/util/syscalltbl.c
+++ b/tools/perf/util/syscalltbl.c
@@ -10,9 +10,9 @@
#include <linux/compiler.h>
#ifdef HAVE_SYSCALL_TABLE_SUPPORT
+#include <linux/zalloc.h>
#include <string.h>
#include "string2.h"
-#include "util.h"
#if defined(__x86_64__)
#include <asm/syscalls_64.c>
diff --git a/tools/perf/util/target.c b/tools/perf/util/target.c
index 3852d07c49bd..3adc65480349 100644
--- a/tools/perf/util/target.c
+++ b/tools/perf/util/target.c
@@ -10,9 +10,9 @@
#include "debug.h"
#include <pwd.h>
+#include <stdlib.h>
#include <string.h>
-
enum target_errno target__validate(struct target *target)
{
enum target_errno ret = TARGET_ERRNO__SUCCESS;
diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
index 6ff1ff4d4ce7..15134ac9b8f1 100644
--- a/tools/perf/util/thread-stack.c
+++ b/tools/perf/util/thread-stack.c
@@ -7,12 +7,13 @@
#include <linux/rbtree.h>
#include <linux/list.h>
#include <linux/log2.h>
+#include <linux/zalloc.h>
#include <errno.h>
+#include <stdlib.h>
#include "thread.h"
#include "event.h"
#include "machine.h"
#include "env.h"
-#include "util.h"
#include "debug.h"
#include "symbol.h"
#include "comm.h"
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 3e29a4e8b5e6..873ab505ca80 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -5,10 +5,10 @@
#include <stdio.h>
#include <string.h>
#include <linux/kernel.h>
+#include <linux/zalloc.h>
#include "session.h"
#include "thread.h"
#include "thread-stack.h"
-#include "util.h"
#include "debug.h"
#include "namespaces.h"
#include "comm.h"
@@ -93,14 +93,14 @@ void thread__delete(struct thread *thread)
down_write(&thread->namespaces_lock);
list_for_each_entry_safe(namespaces, tmp_namespaces,
&thread->namespaces_list, list) {
- list_del(&namespaces->list);
+ list_del_init(&namespaces->list);
namespaces__free(namespaces);
}
up_write(&thread->namespaces_lock);
down_write(&thread->comm_lock);
list_for_each_entry_safe(comm, tmp_comm, &thread->comm_list, list) {
- list_del(&comm->list);
+ list_del_init(&comm->list);
comm__free(comm);
}
up_write(&thread->comm_lock);
diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c
index 281bf06f10f2..5b3511f2b6b1 100644
--- a/tools/perf/util/thread_map.c
+++ b/tools/perf/util/thread_map.c
@@ -13,9 +13,9 @@
#include <string.h>
#include <api/fs/fs.h>
#include <linux/string.h>
+#include <linux/zalloc.h>
#include "asm/bug.h"
#include "thread_map.h"
-#include "util.h"
#include "debug.h"
#include "event.h"
@@ -480,7 +480,7 @@ int thread_map__remove(struct thread_map *threads, int idx)
/*
* Free the 'idx' item and shift the rest up.
*/
- free(threads->map[idx].comm);
+ zfree(&threads->map[idx].comm);
for (i = idx; i < threads->nr - 1; i++)
threads->map[i] = threads->map[i + 1];
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c
index 806a11b334d3..4550015b9d5d 100644
--- a/tools/perf/util/trace-event-info.c
+++ b/tools/perf/util/trace-event-info.c
@@ -18,6 +18,7 @@
#include <stdbool.h>
#include <linux/list.h>
#include <linux/kernel.h>
+#include <linux/zalloc.h>
#include "../perf.h"
#include "trace-event.h"
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c
index b023db136ef3..ba58f69777a1 100644
--- a/tools/perf/util/trace-event-scripting.c
+++ b/tools/perf/util/trace-event-scripting.c
@@ -12,8 +12,8 @@
#include "../perf.h"
#include "debug.h"
-#include "util.h"
#include "trace-event.h"
+#include <linux/zalloc.h>
struct scripting_context *scripting_context;
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 407d0167b942..28f71ca6ce1c 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -12,6 +12,7 @@
#include "symbol.h"
#include "thread.h"
#include <linux/types.h>
+#include <linux/zalloc.h>
#include "event.h"
#include "perf_regs.h"
#include "callchain.h"
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
index 25e1406b1f8b..71a788921b62 100644
--- a/tools/perf/util/unwind-libunwind-local.c
+++ b/tools/perf/util/unwind-libunwind-local.c
@@ -25,6 +25,7 @@
#include <unistd.h>
#include <sys/mman.h>
#include <linux/list.h>
+#include <linux/zalloc.h>
#ifndef REMOTE_UNWIND_LIBUNWIND
#include <libunwind.h>
#include <libunwind-ptrace.h>
@@ -345,7 +346,7 @@ static int read_unwind_spec_debug_frame(struct dso *dso,
__func__,
dso->symsrc_filename,
debuglink);
- free(dso->symsrc_filename);
+ zfree(&dso->symsrc_filename);
}
dso->symsrc_filename = debuglink;
} else {
diff --git a/tools/perf/util/usage.c b/tools/perf/util/usage.c
index 070d25ceea6a..3949a60b00ae 100644
--- a/tools/perf/util/usage.c
+++ b/tools/perf/util/usage.c
@@ -9,6 +9,9 @@
*/
#include "util.h"
#include "debug.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <linux/compiler.h>
static __noreturn void usage_builtin(const char *err)
{
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 125e215dd3d8..dc7a469921e9 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -9,8 +9,6 @@
#include <fcntl.h>
#include <stdbool.h>
#include <stddef.h>
-#include <stdlib.h>
-#include <stdarg.h>
#include <linux/compiler.h>
#include <sys/types.h>
@@ -18,13 +16,6 @@
void usage(const char *err) __noreturn;
void die(const char *err, ...) __noreturn __printf(1, 2);
-static inline void *zalloc(size_t size)
-{
- return calloc(1, size);
-}
-
-#define zfree(ptr) ({ free(*ptr); *ptr = NULL; })
-
struct dirent;
struct nsinfo;
struct strlist;
@@ -59,18 +50,10 @@ int fetch_kernel_version(unsigned int *puint,
const char *perf_tip(const char *dirpath);
-#ifndef HAVE_GET_CURRENT_DIR_NAME
-char *get_current_dir_name(void);
-#endif
-
#ifndef HAVE_SCHED_GETCPU_SUPPORT
int sched_getcpu(void);
#endif
-#ifndef HAVE_SETNS_SUPPORT
-int setns(int fd, int nstype);
-#endif
-
extern bool perf_singlethreaded;
void perf_set_singlethreaded(void);
diff --git a/tools/perf/util/values.c b/tools/perf/util/values.c
index 4b7a303e4ba8..c59154e2d124 100644
--- a/tools/perf/util/values.c
+++ b/tools/perf/util/values.c
@@ -3,8 +3,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
+#include <linux/zalloc.h>
-#include "util.h"
#include "values.h"
#include "debug.h"
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
index 5031b7b22bbd..7f427bab6c12 100644
--- a/tools/perf/util/vdso.c
+++ b/tools/perf/util/vdso.c
@@ -16,6 +16,7 @@
#include "machine.h"
#include "thread.h"
#include "linux/string.h"
+#include <linux/zalloc.h>
#include "debug.h"
/*
diff --git a/tools/perf/util/xyarray.c b/tools/perf/util/xyarray.c
index dc95154f5646..86889ebc3514 100644
--- a/tools/perf/util/xyarray.c
+++ b/tools/perf/util/xyarray.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
#include "xyarray.h"
-#include "util.h"
#include <stdlib.h>
#include <string.h>
+#include <linux/zalloc.h>
struct xyarray *xyarray__new(int xlen, int ylen, size_t entry_size)
{
diff --git a/tools/power/x86/intel-speed-select/.gitignore b/tools/power/x86/intel-speed-select/.gitignore
new file mode 100644
index 000000000000..f61145925ce9
--- /dev/null
+++ b/tools/power/x86/intel-speed-select/.gitignore
@@ -0,0 +1,2 @@
+include/
+intel-speed-select
diff --git a/tools/power/x86/intel-speed-select/Build b/tools/power/x86/intel-speed-select/Build
new file mode 100644
index 000000000000..b61456d75190
--- /dev/null
+++ b/tools/power/x86/intel-speed-select/Build
@@ -0,0 +1 @@
+intel-speed-select-y += isst-config.o isst-core.o isst-display.o
diff --git a/tools/power/x86/intel-speed-select/Makefile b/tools/power/x86/intel-speed-select/Makefile
new file mode 100644
index 000000000000..12c6939dca2a
--- /dev/null
+++ b/tools/power/x86/intel-speed-select/Makefile
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: GPL-2.0
+include ../../../scripts/Makefile.include
+
+bindir ?= /usr/bin
+
+ifeq ($(srctree),)
+srctree := $(patsubst %/,%,$(dir $(CURDIR)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+endif
+
+# Do not use make's built-in rules
+# (this improves performance and avoids hard-to-debug behaviour);
+MAKEFLAGS += -r
+
+override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
+
+ALL_TARGETS := intel-speed-select
+ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
+
+all: $(ALL_PROGRAMS)
+
+export srctree OUTPUT CC LD CFLAGS
+include $(srctree)/tools/build/Makefile.include
+
+#
+# We need the following to be outside of kernel tree
+#
+$(OUTPUT)include/linux/isst_if.h: ../../../../include/uapi/linux/isst_if.h
+ mkdir -p $(OUTPUT)include/linux 2>&1 || true
+ ln -sf $(CURDIR)/../../../../include/uapi/linux/isst_if.h $@
+
+prepare: $(OUTPUT)include/linux/isst_if.h
+
+ISST_IN := $(OUTPUT)intel-speed-select-in.o
+
+$(ISST_IN): prepare FORCE
+ $(Q)$(MAKE) $(build)=intel-speed-select
+$(OUTPUT)intel-speed-select: $(ISST_IN)
+ $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
+
+clean:
+ rm -f $(ALL_PROGRAMS)
+ rm -rf $(OUTPUT)include/linux/isst_if.h
+ find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete
+
+install: $(ALL_PROGRAMS)
+ install -d -m 755 $(DESTDIR)$(bindir); \
+ for program in $(ALL_PROGRAMS); do \
+ install $$program $(DESTDIR)$(bindir); \
+ done
+
+FORCE:
+
+.PHONY: all install clean FORCE prepare
diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c
new file mode 100644
index 000000000000..91c5ad1685a1
--- /dev/null
+++ b/tools/power/x86/intel-speed-select/isst-config.c
@@ -0,0 +1,1607 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Speed Select -- Enumerate and control features
+ * Copyright (c) 2019 Intel Corporation.
+ */
+
+#include <linux/isst_if.h>
+
+#include "isst.h"
+
+struct process_cmd_struct {
+ char *feature;
+ char *command;
+ void (*process_fn)(void);
+};
+
+static const char *version_str = "v1.0";
+static const int supported_api_ver = 1;
+static struct isst_if_platform_info isst_platform_info;
+static char *progname;
+static int debug_flag;
+static FILE *outf;
+
+static int cpu_model;
+
+#define MAX_CPUS_IN_ONE_REQ 64
+static short max_target_cpus;
+static unsigned short target_cpus[MAX_CPUS_IN_ONE_REQ];
+
+static int topo_max_cpus;
+static size_t present_cpumask_size;
+static cpu_set_t *present_cpumask;
+static size_t target_cpumask_size;
+static cpu_set_t *target_cpumask;
+static int tdp_level = 0xFF;
+static int fact_bucket = 0xFF;
+static int fact_avx = 0xFF;
+static unsigned long long fact_trl;
+static int out_format_json;
+static int cmd_help;
+
+/* clos related */
+static int current_clos = -1;
+static int clos_epp = -1;
+static int clos_prop_prio = -1;
+static int clos_min = -1;
+static int clos_max = -1;
+static int clos_desired = -1;
+static int clos_priority_type;
+
+struct _cpu_map {
+ unsigned short core_id;
+ unsigned short pkg_id;
+ unsigned short die_id;
+ unsigned short punit_cpu;
+ unsigned short punit_cpu_core;
+};
+struct _cpu_map *cpu_map;
+
+void debug_printf(const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+
+ if (debug_flag)
+ vprintf(format, args);
+
+ va_end(args);
+}
+
+static void update_cpu_model(void)
+{
+ unsigned int ebx, ecx, edx;
+ unsigned int fms, family;
+
+ __cpuid(1, fms, ebx, ecx, edx);
+ family = (fms >> 8) & 0xf;
+ cpu_model = (fms >> 4) & 0xf;
+ if (family == 6 || family == 0xf)
+ cpu_model += ((fms >> 16) & 0xf) << 4;
+}
+
+/* Open a file, and exit on failure */
+static FILE *fopen_or_exit(const char *path, const char *mode)
+{
+ FILE *filep = fopen(path, mode);
+
+ if (!filep)
+ err(1, "%s: open failed", path);
+
+ return filep;
+}
+
+/* Parse a file containing a single int */
+static int parse_int_file(int fatal, const char *fmt, ...)
+{
+ va_list args;
+ char path[PATH_MAX];
+ FILE *filep;
+ int value;
+
+ va_start(args, fmt);
+ vsnprintf(path, sizeof(path), fmt, args);
+ va_end(args);
+ if (fatal) {
+ filep = fopen_or_exit(path, "r");
+ } else {
+ filep = fopen(path, "r");
+ if (!filep)
+ return -1;
+ }
+ if (fscanf(filep, "%d", &value) != 1)
+ err(1, "%s: failed to parse number from file", path);
+ fclose(filep);
+
+ return value;
+}
+
+int cpufreq_sysfs_present(void)
+{
+ DIR *dir;
+
+ dir = opendir("/sys/devices/system/cpu/cpu0/cpufreq");
+ if (dir) {
+ closedir(dir);
+ return 1;
+ }
+
+ return 0;
+}
+
+int out_format_is_json(void)
+{
+ return out_format_json;
+}
+
+int get_physical_package_id(int cpu)
+{
+ return parse_int_file(
+ 1, "/sys/devices/system/cpu/cpu%d/topology/physical_package_id",
+ cpu);
+}
+
+int get_physical_core_id(int cpu)
+{
+ return parse_int_file(
+ 1, "/sys/devices/system/cpu/cpu%d/topology/core_id", cpu);
+}
+
+int get_physical_die_id(int cpu)
+{
+ int ret;
+
+ ret = parse_int_file(0, "/sys/devices/system/cpu/cpu%d/topology/die_id",
+ cpu);
+ if (ret < 0)
+ ret = 0;
+
+ return ret;
+}
+
+int get_topo_max_cpus(void)
+{
+ return topo_max_cpus;
+}
+
+#define MAX_PACKAGE_COUNT 8
+#define MAX_DIE_PER_PACKAGE 2
+static void for_each_online_package_in_set(void (*callback)(int, void *, void *,
+ void *, void *),
+ void *arg1, void *arg2, void *arg3,
+ void *arg4)
+{
+ int max_packages[MAX_PACKAGE_COUNT * MAX_PACKAGE_COUNT];
+ int pkg_index = 0, i;
+
+ memset(max_packages, 0xff, sizeof(max_packages));
+ for (i = 0; i < topo_max_cpus; ++i) {
+ int j, online, pkg_id, die_id = 0, skip = 0;
+
+ if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask))
+ continue;
+ if (i)
+ online = parse_int_file(
+ 1, "/sys/devices/system/cpu/cpu%d/online", i);
+ else
+ online =
+ 1; /* online entry for CPU 0 needs some special configs */
+
+ die_id = get_physical_die_id(i);
+ if (die_id < 0)
+ die_id = 0;
+ pkg_id = get_physical_package_id(i);
+ /* Create an unique id for package, die combination to store */
+ pkg_id = (MAX_PACKAGE_COUNT * pkg_id + die_id);
+
+ for (j = 0; j < pkg_index; ++j) {
+ if (max_packages[j] == pkg_id) {
+ skip = 1;
+ break;
+ }
+ }
+
+ if (!skip && online && callback) {
+ callback(i, arg1, arg2, arg3, arg4);
+ max_packages[pkg_index++] = pkg_id;
+ }
+ }
+}
+
+static void for_each_online_target_cpu_in_set(
+ void (*callback)(int, void *, void *, void *, void *), void *arg1,
+ void *arg2, void *arg3, void *arg4)
+{
+ int i;
+
+ for (i = 0; i < topo_max_cpus; ++i) {
+ int online;
+
+ if (!CPU_ISSET_S(i, target_cpumask_size, target_cpumask))
+ continue;
+ if (i)
+ online = parse_int_file(
+ 1, "/sys/devices/system/cpu/cpu%d/online", i);
+ else
+ online =
+ 1; /* online entry for CPU 0 needs some special configs */
+
+ if (online && callback)
+ callback(i, arg1, arg2, arg3, arg4);
+ }
+}
+
+#define BITMASK_SIZE 32
+static void set_max_cpu_num(void)
+{
+ FILE *filep;
+ unsigned long dummy;
+
+ topo_max_cpus = 0;
+ filep = fopen_or_exit(
+ "/sys/devices/system/cpu/cpu0/topology/thread_siblings", "r");
+ while (fscanf(filep, "%lx,", &dummy) == 1)
+ topo_max_cpus += BITMASK_SIZE;
+ fclose(filep);
+ topo_max_cpus--; /* 0 based */
+
+ debug_printf("max cpus %d\n", topo_max_cpus);
+}
+
+size_t alloc_cpu_set(cpu_set_t **cpu_set)
+{
+ cpu_set_t *_cpu_set;
+ size_t size;
+
+ _cpu_set = CPU_ALLOC((topo_max_cpus + 1));
+ if (_cpu_set == NULL)
+ err(3, "CPU_ALLOC");
+ size = CPU_ALLOC_SIZE((topo_max_cpus + 1));
+ CPU_ZERO_S(size, _cpu_set);
+
+ *cpu_set = _cpu_set;
+ return size;
+}
+
+void free_cpu_set(cpu_set_t *cpu_set)
+{
+ CPU_FREE(cpu_set);
+}
+
+static int cpu_cnt[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE];
+static void set_cpu_present_cpu_mask(void)
+{
+ size_t size;
+ DIR *dir;
+ int i;
+
+ size = alloc_cpu_set(&present_cpumask);
+ present_cpumask_size = size;
+ for (i = 0; i < topo_max_cpus; ++i) {
+ char buffer[256];
+
+ snprintf(buffer, sizeof(buffer),
+ "/sys/devices/system/cpu/cpu%d", i);
+ dir = opendir(buffer);
+ if (dir) {
+ int pkg_id, die_id;
+
+ CPU_SET_S(i, size, present_cpumask);
+ die_id = get_physical_die_id(i);
+ if (die_id < 0)
+ die_id = 0;
+
+ pkg_id = get_physical_package_id(i);
+ if (pkg_id < MAX_PACKAGE_COUNT &&
+ die_id < MAX_DIE_PER_PACKAGE)
+ cpu_cnt[pkg_id][die_id]++;
+ }
+ closedir(dir);
+ }
+}
+
+int get_cpu_count(int pkg_id, int die_id)
+{
+ if (pkg_id < MAX_PACKAGE_COUNT && die_id < MAX_DIE_PER_PACKAGE)
+ return cpu_cnt[pkg_id][die_id] + 1;
+
+ return 0;
+}
+
+static void set_cpu_target_cpu_mask(void)
+{
+ size_t size;
+ int i;
+
+ size = alloc_cpu_set(&target_cpumask);
+ target_cpumask_size = size;
+ for (i = 0; i < max_target_cpus; ++i) {
+ if (!CPU_ISSET_S(target_cpus[i], present_cpumask_size,
+ present_cpumask))
+ continue;
+
+ CPU_SET_S(target_cpus[i], size, target_cpumask);
+ }
+}
+
+static void create_cpu_map(void)
+{
+ const char *pathname = "/dev/isst_interface";
+ int i, fd = 0;
+ struct isst_if_cpu_maps map;
+
+ cpu_map = malloc(sizeof(*cpu_map) * topo_max_cpus);
+ if (!cpu_map)
+ err(3, "cpumap");
+
+ fd = open(pathname, O_RDWR);
+ if (fd < 0)
+ err(-1, "%s open failed", pathname);
+
+ for (i = 0; i < topo_max_cpus; ++i) {
+ if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask))
+ continue;
+
+ map.cmd_count = 1;
+ map.cpu_map[0].logical_cpu = i;
+
+ debug_printf(" map logical_cpu:%d\n",
+ map.cpu_map[0].logical_cpu);
+ if (ioctl(fd, ISST_IF_GET_PHY_ID, &map) == -1) {
+ perror("ISST_IF_GET_PHY_ID");
+ fprintf(outf, "Error: map logical_cpu:%d\n",
+ map.cpu_map[0].logical_cpu);
+ continue;
+ }
+ cpu_map[i].core_id = get_physical_core_id(i);
+ cpu_map[i].pkg_id = get_physical_package_id(i);
+ cpu_map[i].die_id = get_physical_die_id(i);
+ cpu_map[i].punit_cpu = map.cpu_map[0].physical_cpu;
+ cpu_map[i].punit_cpu_core = (map.cpu_map[0].physical_cpu >>
+ 1); // shift to get core id
+
+ debug_printf(
+ "map logical_cpu:%d core: %d die:%d pkg:%d punit_cpu:%d punit_core:%d\n",
+ i, cpu_map[i].core_id, cpu_map[i].die_id,
+ cpu_map[i].pkg_id, cpu_map[i].punit_cpu,
+ cpu_map[i].punit_cpu_core);
+ }
+
+ if (fd)
+ close(fd);
+}
+
+int find_logical_cpu(int pkg_id, int die_id, int punit_core_id)
+{
+ int i;
+
+ for (i = 0; i < topo_max_cpus; ++i) {
+ if (cpu_map[i].pkg_id == pkg_id &&
+ cpu_map[i].die_id == die_id &&
+ cpu_map[i].punit_cpu_core == punit_core_id)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+void set_cpu_mask_from_punit_coremask(int cpu, unsigned long long core_mask,
+ size_t core_cpumask_size,
+ cpu_set_t *core_cpumask, int *cpu_cnt)
+{
+ int i, cnt = 0;
+ int die_id, pkg_id;
+
+ *cpu_cnt = 0;
+ die_id = get_physical_die_id(cpu);
+ pkg_id = get_physical_package_id(cpu);
+
+ for (i = 0; i < 64; ++i) {
+ if (core_mask & BIT(i)) {
+ int j;
+
+ for (j = 0; j < topo_max_cpus; ++j) {
+ if (cpu_map[j].pkg_id == pkg_id &&
+ cpu_map[j].die_id == die_id &&
+ cpu_map[j].punit_cpu_core == i) {
+ CPU_SET_S(j, core_cpumask_size,
+ core_cpumask);
+ ++cnt;
+ }
+ }
+ }
+ }
+
+ *cpu_cnt = cnt;
+}
+
+int find_phy_core_num(int logical_cpu)
+{
+ if (logical_cpu < topo_max_cpus)
+ return cpu_map[logical_cpu].punit_cpu_core;
+
+ return -EINVAL;
+}
+
+static int isst_send_mmio_command(unsigned int cpu, unsigned int reg, int write,
+ unsigned int *value)
+{
+ struct isst_if_io_regs io_regs;
+ const char *pathname = "/dev/isst_interface";
+ int cmd;
+ int fd;
+
+ debug_printf("mmio_cmd cpu:%d reg:%d write:%d\n", cpu, reg, write);
+
+ fd = open(pathname, O_RDWR);
+ if (fd < 0)
+ err(-1, "%s open failed", pathname);
+
+ io_regs.req_count = 1;
+ io_regs.io_reg[0].logical_cpu = cpu;
+ io_regs.io_reg[0].reg = reg;
+ cmd = ISST_IF_IO_CMD;
+ if (write) {
+ io_regs.io_reg[0].read_write = 1;
+ io_regs.io_reg[0].value = *value;
+ } else {
+ io_regs.io_reg[0].read_write = 0;
+ }
+
+ if (ioctl(fd, cmd, &io_regs) == -1) {
+ perror("ISST_IF_IO_CMD");
+ fprintf(outf, "Error: mmio_cmd cpu:%d reg:%x read_write:%x\n",
+ cpu, reg, write);
+ } else {
+ if (!write)
+ *value = io_regs.io_reg[0].value;
+
+ debug_printf(
+ "mmio_cmd response: cpu:%d reg:%x rd_write:%x resp:%x\n",
+ cpu, reg, write, *value);
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+int isst_send_mbox_command(unsigned int cpu, unsigned char command,
+ unsigned char sub_command, unsigned int parameter,
+ unsigned int req_data, unsigned int *resp)
+{
+ const char *pathname = "/dev/isst_interface";
+ int fd;
+ struct isst_if_mbox_cmds mbox_cmds = { 0 };
+
+ debug_printf(
+ "mbox_send: cpu:%d command:%x sub_command:%x parameter:%x req_data:%x\n",
+ cpu, command, sub_command, parameter, req_data);
+
+ if (isst_platform_info.mmio_supported && command == CONFIG_CLOS) {
+ unsigned int value;
+ int write = 0;
+ int clos_id, core_id, ret = 0;
+
+ debug_printf("CLOS %d\n", cpu);
+
+ if (parameter & BIT(MBOX_CMD_WRITE_BIT)) {
+ value = req_data;
+ write = 1;
+ }
+
+ switch (sub_command) {
+ case CLOS_PQR_ASSOC:
+ core_id = parameter & 0xff;
+ ret = isst_send_mmio_command(
+ cpu, PQR_ASSOC_OFFSET + core_id * 4, write,
+ &value);
+ if (!ret && !write)
+ *resp = value;
+ break;
+ case CLOS_PM_CLOS:
+ clos_id = parameter & 0x03;
+ ret = isst_send_mmio_command(
+ cpu, PM_CLOS_OFFSET + clos_id * 4, write,
+ &value);
+ if (!ret && !write)
+ *resp = value;
+ break;
+ case CLOS_PM_QOS_CONFIG:
+ ret = isst_send_mmio_command(cpu, PM_QOS_CONFIG_OFFSET,
+ write, &value);
+ if (!ret && !write)
+ *resp = value;
+ break;
+ case CLOS_STATUS:
+ break;
+ default:
+ break;
+ }
+ return ret;
+ }
+
+ mbox_cmds.cmd_count = 1;
+ mbox_cmds.mbox_cmd[0].logical_cpu = cpu;
+ mbox_cmds.mbox_cmd[0].command = command;
+ mbox_cmds.mbox_cmd[0].sub_command = sub_command;
+ mbox_cmds.mbox_cmd[0].parameter = parameter;
+ mbox_cmds.mbox_cmd[0].req_data = req_data;
+
+ fd = open(pathname, O_RDWR);
+ if (fd < 0)
+ err(-1, "%s open failed", pathname);
+
+ if (ioctl(fd, ISST_IF_MBOX_COMMAND, &mbox_cmds) == -1) {
+ perror("ISST_IF_MBOX_COMMAND");
+ fprintf(outf,
+ "Error: mbox_cmd cpu:%d command:%x sub_command:%x parameter:%x req_data:%x\n",
+ cpu, command, sub_command, parameter, req_data);
+ } else {
+ *resp = mbox_cmds.mbox_cmd[0].resp_data;
+ debug_printf(
+ "mbox_cmd response: cpu:%d command:%x sub_command:%x parameter:%x req_data:%x resp:%x\n",
+ cpu, command, sub_command, parameter, req_data, *resp);
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+int isst_send_msr_command(unsigned int cpu, unsigned int msr, int write,
+ unsigned long long *req_resp)
+{
+ struct isst_if_msr_cmds msr_cmds;
+ const char *pathname = "/dev/isst_interface";
+ int fd;
+
+ fd = open(pathname, O_RDWR);
+ if (fd < 0)
+ err(-1, "%s open failed", pathname);
+
+ msr_cmds.cmd_count = 1;
+ msr_cmds.msr_cmd[0].logical_cpu = cpu;
+ msr_cmds.msr_cmd[0].msr = msr;
+ msr_cmds.msr_cmd[0].read_write = write;
+ if (write)
+ msr_cmds.msr_cmd[0].data = *req_resp;
+
+ if (ioctl(fd, ISST_IF_MSR_COMMAND, &msr_cmds) == -1) {
+ perror("ISST_IF_MSR_COMMAD");
+ fprintf(outf, "Error: msr_cmd cpu:%d msr:%x read_write:%d\n",
+ cpu, msr, write);
+ } else {
+ if (!write)
+ *req_resp = msr_cmds.msr_cmd[0].data;
+
+ debug_printf(
+ "msr_cmd response: cpu:%d msr:%x rd_write:%x resp:%llx %llx\n",
+ cpu, msr, write, *req_resp, msr_cmds.msr_cmd[0].data);
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+static int isst_fill_platform_info(void)
+{
+ const char *pathname = "/dev/isst_interface";
+ int fd;
+
+ fd = open(pathname, O_RDWR);
+ if (fd < 0)
+ err(-1, "%s open failed", pathname);
+
+ if (ioctl(fd, ISST_IF_GET_PLATFORM_INFO, &isst_platform_info) == -1) {
+ perror("ISST_IF_GET_PLATFORM_INFO");
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+static void isst_print_platform_information(void)