* [PATCH v5 55/58] perf Makefile: Update Python script installation path
From: Ian Rogers @ 2026-04-24 16:47 UTC (permalink / raw)
To: acme, adrian.hunter, james.clark, leo.yan, namhyung, tmricht
Cc: alice.mei.rogers, dapeng1.mi, linux-arm-kernel, linux-kernel,
linux-perf-users, mingo, peterz, Ian Rogers
In-Reply-To: <20260424164721.2229025-1-irogers@google.com>
Remove libpython feature test that is now a python-module feature
test. Update references from libpython to python-module accordingly.
Remove references to legacy 'scripts/python' directory and install
scripts directly to 'python' directory under libexec. This aligns with
the removal of embedded interpreter and move to standalone
scripts.
Assisted-by: Gemini:gemini-3.1-pro-preview
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/build/Makefile.feature | 4 ++--
tools/build/feature/Makefile | 4 ++--
tools/build/feature/test-all.c | 6 +++---
tools/build/feature/test-libpython.c | 10 ----------
tools/build/feature/test-python-module.c | 12 ++++++++++++
tools/perf/Makefile.config | 13 ++++++-------
tools/perf/Makefile.perf | 10 ++++------
tools/perf/tests/make | 5 +----
8 files changed, 30 insertions(+), 34 deletions(-)
delete mode 100644 tools/build/feature/test-libpython.c
create mode 100644 tools/build/feature/test-python-module.c
diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
index 96d4382144c4..cbe41ba7bae5 100644
--- a/tools/build/Makefile.feature
+++ b/tools/build/Makefile.feature
@@ -79,7 +79,7 @@ FEATURE_TESTS_BASIC := \
libelf-zstd \
libnuma \
numa_num_possible_cpus \
- libpython \
+ python-module \
libslang \
libtraceevent \
libcpupower \
@@ -140,7 +140,7 @@ FEATURE_DISPLAY ?= \
libelf \
libnuma \
numa_num_possible_cpus \
- libpython \
+ python-module \
libcapstone \
llvm-perf \
zlib \
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
index 60e3df8142a5..5530f9e03fcf 100644
--- a/tools/build/feature/Makefile
+++ b/tools/build/feature/Makefile
@@ -30,7 +30,7 @@ FILES= \
test-libdebuginfod.bin \
test-libnuma.bin \
test-numa_num_possible_cpus.bin \
- test-libpython.bin \
+ test-python-module.bin \
test-libslang.bin \
test-libtraceevent.bin \
test-libcpupower.bin \
@@ -252,7 +252,7 @@ $(OUTPUT)test-gtk2-infobar.bin:
grep-libs = $(filter -l%,$(1))
strip-libs = $(filter-out -l%,$(1))
-$(OUTPUT)test-libpython.bin:
+$(OUTPUT)test-python-module.bin:
$(BUILD) $(FLAGS_PYTHON_EMBED)
$(OUTPUT)test-libbfd.bin:
diff --git a/tools/build/feature/test-all.c b/tools/build/feature/test-all.c
index 1488bf6e6078..4400e3d24f81 100644
--- a/tools/build/feature/test-all.c
+++ b/tools/build/feature/test-all.c
@@ -10,8 +10,8 @@
* Quirk: Python headers cannot be in arbitrary places, so keep this testcase at
* the top:
*/
-#define main main_test_libpython
-# include "test-libpython.c"
+#define main main_test_python_module
+# include "test-python-module.c"
#undef main
#define main main_test_hello
@@ -148,7 +148,7 @@
int main(int argc, char *argv[])
{
- main_test_libpython();
+ main_test_python_module();
main_test_hello();
main_test_libelf();
main_test_gettid();
diff --git a/tools/build/feature/test-libpython.c b/tools/build/feature/test-libpython.c
deleted file mode 100644
index 371c9113e49d..000000000000
--- a/tools/build/feature/test-libpython.c
+++ /dev/null
@@ -1,10 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <Python.h>
-
-int main(void)
-{
- Py_Initialize();
-
- return 0;
-}
-#undef _GNU_SOURCE
diff --git a/tools/build/feature/test-python-module.c b/tools/build/feature/test-python-module.c
new file mode 100644
index 000000000000..d670dba014b0
--- /dev/null
+++ b/tools/build/feature/test-python-module.c
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <Python.h>
+
+int main(void)
+{
+ static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ };
+ PyObject *module = PyModule_Create(&moduledef);
+
+ return module ? 0 : -1;
+}
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index ecddd91229c8..e2cef452964f 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -317,7 +317,7 @@ PYTHON_CONFIG_SQ := $(call shell-sq,$(PYTHON_CONFIG))
# Python 3.8 changed the output of `python-config --ldflags` to not include the
# '-lpythonX.Y' flag unless '--embed' is also passed. The feature check for
-# libpython fails if that flag is not included in LDFLAGS
+# python-module fails if that flag is not included in LDFLAGS
ifeq ($(shell $(PYTHON_CONFIG_SQ) --ldflags --embed 2>&1 1>/dev/null; echo $$?), 0)
PYTHON_CONFIG_LDFLAGS := --ldflags --embed
else
@@ -340,8 +340,8 @@ ifdef PYTHON_CONFIG
endif
endif
-FEATURE_CHECK_CFLAGS-libpython := $(PYTHON_EMBED_CCOPTS)
-FEATURE_CHECK_LDFLAGS-libpython := $(PYTHON_EMBED_LDOPTS)
+FEATURE_CHECK_CFLAGS-python-module := $(PYTHON_EMBED_CCOPTS)
+FEATURE_CHECK_LDFLAGS-python-module := $(PYTHON_EMBED_LDOPTS)
FEATURE_CHECK_LDFLAGS-libaio = -lrt
@@ -830,13 +830,12 @@ endif
disable-python = $(eval $(disable-python_code))
define disable-python_code
- CFLAGS += -DNO_LIBPYTHON
$(warning $1)
- NO_LIBPYTHON := 1
+ NO_PYTHON_MODULE := 1
endef
PYTHON_EXTENSION_SUFFIX := '.so'
-ifdef NO_LIBPYTHON
+ifdef NO_PYTHON_MODULE
$(call disable-python,Python support disabled by user)
else
@@ -849,7 +848,7 @@ else
$(call disable-python,No 'python-config' tool was found: disables Python support - please install python-devel/python-dev)
else
- ifneq ($(feature-libpython), 1)
+ ifneq ($(feature-python-module), 1)
$(call disable-python,No 'Python.h' was found: disables Python support - please install python-devel/python-dev)
else
PYTHON_SETUPTOOLS_INSTALLED := $(shell $(PYTHON) -c 'import setuptools;' 2> /dev/null && echo "yes" || echo "no")
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 2020532bab9c..e50b1e8cf85d 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -17,9 +17,7 @@ include ../scripts/utilities.mak
#
# Define CROSS_COMPILE as prefix name of compiler if you want cross-builds.
#
-
-#
-# Define NO_LIBPYTHON to disable python script extension.
+# Define NO_PYTHON_MODULE to disable python script extension.
#
# Define PYTHON to point to the python binary if the default
# `python' is not correct; for example: PYTHON=python2
@@ -1099,10 +1097,10 @@ endif
$(call QUIET_INSTALL, perf-iostat) \
$(INSTALL) $(OUTPUT)perf-iostat -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
-ifndef NO_LIBPYTHON
+ifndef NO_PYTHON_MODULE
$(call QUIET_INSTALL, python-scripts) \
- $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python'; \
- $(INSTALL) python/*.py -m 644 -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python'
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/python'; \
+ $(INSTALL) python/*.py -m 644 -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/python'
endif
$(call QUIET_INSTALL, dlfilters) \
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/dlfilters'; \
diff --git a/tools/perf/tests/make b/tools/perf/tests/make
index 31b064928cfc..f2c5f1c254a7 100644
--- a/tools/perf/tests/make
+++ b/tools/perf/tests/make
@@ -75,8 +75,6 @@ make_jevents_all := JEVENTS_ARCH=all
make_no_bpf_skel := BUILD_BPF_SKEL=0
make_gen_vmlinux_h := GEN_VMLINUX_H=1
-make_no_libpython := NO_LIBPYTHON=1
-make_no_scripts := NO_LIBPYTHON=1
make_no_slang := NO_SLANG=1
make_no_gtk2 := NO_GTK2=1
make_no_ui := NO_SLANG=1 NO_GTK2=1
@@ -118,7 +116,7 @@ make_install_prefix_slash := install prefix=/tmp/krava/
make_static := LDFLAGS=-static NO_PERF_READ_VDSO32=1 NO_PERF_READ_VDSOX32=1 NO_JVMTI=1 NO_LIBTRACEEVENT=1 NO_LIBELF=1
# all the NO_* variable combined
-make_minimal := NO_LIBPYTHON=1 NO_GTK2=1
+make_minimal := NO_GTK2=1
make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_BACKTRACE=1
make_minimal += NO_LIBNUMA=1 NO_LIBBIONIC=1 NO_LIBDW=1
make_minimal += NO_LIBBPF=1
@@ -150,7 +148,6 @@ run += make_jevents_all
run += make_no_bpf_skel
run += make_gen_vmlinux_h
-run += make_no_libpython
run += make_no_scripts
run += make_no_slang
run += make_no_gtk2
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v5 53/58] perf: Remove libperl support, legacy Perl scripts and tests
From: Ian Rogers @ 2026-04-24 16:47 UTC (permalink / raw)
To: acme, adrian.hunter, james.clark, leo.yan, namhyung, tmricht
Cc: alice.mei.rogers, dapeng1.mi, linux-arm-kernel, linux-kernel,
linux-perf-users, mingo, peterz, Ian Rogers
In-Reply-To: <20260424164721.2229025-1-irogers@google.com>
This commit removes embedded Perl interpreter support from perf, as all
legacy Perl scripts have been ported to Python or are no longer needed.
Changes include:
- Removal of libperl feature detection and flags from Makefile.config.
- Removal of Perl script installation rules from Makefile.perf.
- Removal of build rules for trace-event-perl.o and Perf-Trace-Util.
- Deletion of tools/perf/util/scripting-engines/trace-event-perl.c.
- Removal of Perl scripting operations and setup from
trace-event-scripting.c.
- Removal of setup_perl_scripting() call from builtin-script.c and
declaration from trace-event.h.
- Removal of Perl checks in the script browser (scripts.c).
- Removal of libperl from the supported features list in
builtin-check.c and Documentation/perf-check.txt.
- Removal of make_libperl target from tests/make.
- Deletion of the entire tools/perf/scripts/perl directory containing
legacy Perl scripts.
- Removal of tools/perf/tests/shell/script_perl.sh test.
Assisted-by: Gemini:gemini-3.1-pro-preview
Signed-off-by: Ian Rogers <irogers@google.com>
---
v2:
1. In tools/perf/ui/browsers/scripts.c : I added an unconditional skip
for the "perl" directory in find_scripts() . This ensures that even
if stale Perl scripts are left over in the installation directory,
they will not appear in the TUI script browser.
2. In tools/perf/Makefile.config : I removed the dangling $(call
detected_var,PERL_EMBED_CCOPTS) line at the end of the file, so it
no longer exports that undefined variable to the configuration.
---
tools/build/Makefile.feature | 1 -
tools/build/feature/Makefile | 19 +-
tools/build/feature/test-libperl.c | 10 -
tools/perf/Documentation/perf-check.txt | 1 -
tools/perf/Makefile.config | 22 +-
tools/perf/Makefile.perf | 11 +-
tools/perf/builtin-check.c | 2 +-
tools/perf/builtin-script.c | 4 +-
tools/perf/scripts/Build | 4 +-
tools/perf/scripts/perl/Perf-Trace-Util/Build | 9 -
.../scripts/perl/Perf-Trace-Util/Context.c | 122 ---
.../scripts/perl/Perf-Trace-Util/Context.xs | 42 -
.../scripts/perl/Perf-Trace-Util/Makefile.PL | 18 -
.../perf/scripts/perl/Perf-Trace-Util/README | 59 --
.../Perf-Trace-Util/lib/Perf/Trace/Context.pm | 55 --
.../Perf-Trace-Util/lib/Perf/Trace/Core.pm | 192 -----
.../Perf-Trace-Util/lib/Perf/Trace/Util.pm | 94 ---
.../perf/scripts/perl/Perf-Trace-Util/typemap | 1 -
.../scripts/perl/bin/check-perf-trace-record | 2 -
.../scripts/perl/bin/failed-syscalls-record | 3 -
.../scripts/perl/bin/failed-syscalls-report | 10 -
tools/perf/scripts/perl/bin/rw-by-file-record | 3 -
tools/perf/scripts/perl/bin/rw-by-file-report | 10 -
tools/perf/scripts/perl/bin/rw-by-pid-record | 2 -
tools/perf/scripts/perl/bin/rw-by-pid-report | 3 -
tools/perf/scripts/perl/bin/rwtop-record | 2 -
tools/perf/scripts/perl/bin/rwtop-report | 20 -
.../scripts/perl/bin/wakeup-latency-record | 6 -
.../scripts/perl/bin/wakeup-latency-report | 3 -
tools/perf/scripts/perl/check-perf-trace.pl | 106 ---
tools/perf/scripts/perl/failed-syscalls.pl | 47 --
tools/perf/scripts/perl/rw-by-file.pl | 106 ---
tools/perf/scripts/perl/rw-by-pid.pl | 184 -----
tools/perf/scripts/perl/rwtop.pl | 203 -----
tools/perf/scripts/perl/wakeup-latency.pl | 107 ---
tools/perf/tests/make | 4 +-
tools/perf/tests/shell/script_perl.sh | 102 ---
tools/perf/ui/browsers/scripts.c | 7 +-
tools/perf/util/scripting-engines/Build | 6 +-
.../util/scripting-engines/trace-event-perl.c | 773 ------------------
tools/perf/util/trace-event-scripting.c | 65 --
tools/perf/util/trace-event.h | 2 +-
42 files changed, 15 insertions(+), 2427 deletions(-)
delete mode 100644 tools/build/feature/test-libperl.c
delete mode 100644 tools/perf/scripts/perl/Perf-Trace-Util/Build
delete mode 100644 tools/perf/scripts/perl/Perf-Trace-Util/Context.c
delete mode 100644 tools/perf/scripts/perl/Perf-Trace-Util/Context.xs
delete mode 100644 tools/perf/scripts/perl/Perf-Trace-Util/Makefile.PL
delete mode 100644 tools/perf/scripts/perl/Perf-Trace-Util/README
delete mode 100644 tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm
delete mode 100644 tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm
delete mode 100644 tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm
delete mode 100644 tools/perf/scripts/perl/Perf-Trace-Util/typemap
delete mode 100644 tools/perf/scripts/perl/bin/check-perf-trace-record
delete mode 100644 tools/perf/scripts/perl/bin/failed-syscalls-record
delete mode 100644 tools/perf/scripts/perl/bin/failed-syscalls-report
delete mode 100644 tools/perf/scripts/perl/bin/rw-by-file-record
delete mode 100644 tools/perf/scripts/perl/bin/rw-by-file-report
delete mode 100644 tools/perf/scripts/perl/bin/rw-by-pid-record
delete mode 100644 tools/perf/scripts/perl/bin/rw-by-pid-report
delete mode 100644 tools/perf/scripts/perl/bin/rwtop-record
delete mode 100644 tools/perf/scripts/perl/bin/rwtop-report
delete mode 100644 tools/perf/scripts/perl/bin/wakeup-latency-record
delete mode 100644 tools/perf/scripts/perl/bin/wakeup-latency-report
delete mode 100644 tools/perf/scripts/perl/check-perf-trace.pl
delete mode 100644 tools/perf/scripts/perl/failed-syscalls.pl
delete mode 100644 tools/perf/scripts/perl/rw-by-file.pl
delete mode 100644 tools/perf/scripts/perl/rw-by-pid.pl
delete mode 100644 tools/perf/scripts/perl/rwtop.pl
delete mode 100644 tools/perf/scripts/perl/wakeup-latency.pl
delete mode 100755 tools/perf/tests/shell/script_perl.sh
delete mode 100644 tools/perf/util/scripting-engines/trace-event-perl.c
diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
index 0b7a7c38cb88..96d4382144c4 100644
--- a/tools/build/Makefile.feature
+++ b/tools/build/Makefile.feature
@@ -118,7 +118,6 @@ FEATURE_TESTS_EXTRA := \
libbfd-liberty \
libbfd-liberty-z \
libopencsd \
- libperl \
cxx \
llvm \
clang \
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
index f163a245837a..60e3df8142a5 100644
--- a/tools/build/feature/Makefile
+++ b/tools/build/feature/Makefile
@@ -30,7 +30,6 @@ FILES= \
test-libdebuginfod.bin \
test-libnuma.bin \
test-numa_num_possible_cpus.bin \
- test-libperl.bin \
test-libpython.bin \
test-libslang.bin \
test-libtraceevent.bin \
@@ -113,7 +112,7 @@ __BUILD = $(CC) $(CFLAGS) -MD -Wall -Werror -o $@ $(patsubst %.bin,%.c,$(@F)) $(
BUILD = $(__BUILD) > $(@:.bin=.make.output) 2>&1
BUILD_BFD = $(BUILD) -DPACKAGE='"perf"' -lbfd -ldl
BUILD_ALL = $(BUILD) -fstack-protector-all -O2 -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -lslang \
- $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -ldl -lz -llzma -lzstd \
+ $(FLAGS_PYTHON_EMBED) -ldl -lz -llzma -lzstd \
$(shell $(PKG_CONFIG) --libs --cflags openssl 2>/dev/null)
__BUILDXX = $(CXX) $(CXXFLAGS) -MD -Wall -Werror -o $@ $(patsubst %.bin,%.cpp,$(@F)) $(LDFLAGS)
@@ -253,22 +252,6 @@ $(OUTPUT)test-gtk2-infobar.bin:
grep-libs = $(filter -l%,$(1))
strip-libs = $(filter-out -l%,$(1))
-PERL_EMBED_LDOPTS = $(shell perl -MExtUtils::Embed -e ldopts 2>/dev/null)
-PERL_EMBED_LDFLAGS = $(call strip-libs,$(PERL_EMBED_LDOPTS))
-PERL_EMBED_LIBADD = $(call grep-libs,$(PERL_EMBED_LDOPTS))
-PERL_EMBED_CCOPTS = $(shell perl -MExtUtils::Embed -e ccopts 2>/dev/null)
-FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS)
-
-ifeq ($(CC_NO_CLANG), 0)
- PERL_EMBED_LDOPTS := $(filter-out -specs=%,$(PERL_EMBED_LDOPTS))
- PERL_EMBED_CCOPTS := $(filter-out -flto=auto -ffat-lto-objects, $(PERL_EMBED_CCOPTS))
- PERL_EMBED_CCOPTS := $(filter-out -specs=%,$(PERL_EMBED_CCOPTS))
- FLAGS_PERL_EMBED += -Wno-compound-token-split-by-macro
-endif
-
-$(OUTPUT)test-libperl.bin:
- $(BUILD) $(FLAGS_PERL_EMBED)
-
$(OUTPUT)test-libpython.bin:
$(BUILD) $(FLAGS_PYTHON_EMBED)
diff --git a/tools/build/feature/test-libperl.c b/tools/build/feature/test-libperl.c
deleted file mode 100644
index 0415f437eb31..000000000000
--- a/tools/build/feature/test-libperl.c
+++ /dev/null
@@ -1,10 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <EXTERN.h>
-#include <perl.h>
-
-int main(void)
-{
- perl_alloc();
-
- return 0;
-}
diff --git a/tools/perf/Documentation/perf-check.txt b/tools/perf/Documentation/perf-check.txt
index 09e1d35677f5..60fa9ea43a58 100644
--- a/tools/perf/Documentation/perf-check.txt
+++ b/tools/perf/Documentation/perf-check.txt
@@ -58,7 +58,6 @@ feature::
libLLVM / HAVE_LIBLLVM_SUPPORT
libnuma / HAVE_LIBNUMA_SUPPORT
libopencsd / HAVE_CSTRACE_SUPPORT
- libperl / HAVE_LIBPERL_SUPPORT
libpfm4 / HAVE_LIBPFM
libpython / HAVE_LIBPYTHON_SUPPORT
libslang / HAVE_SLANG_SUPPORT
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index 333ddd0e4bd8..db30e73c5efc 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -820,26 +820,7 @@ ifdef GTK2
endif
endif
-ifdef LIBPERL
- PERL_EMBED_LDOPTS = $(shell perl -MExtUtils::Embed -e ldopts 2>/dev/null)
- PERL_EMBED_LDFLAGS = $(call strip-libs,$(PERL_EMBED_LDOPTS))
- PERL_EMBED_LIBADD = $(call grep-libs,$(PERL_EMBED_LDOPTS))
- PERL_EMBED_CCOPTS = $(shell perl -MExtUtils::Embed -e ccopts 2>/dev/null)
- PERL_EMBED_CCOPTS := $(filter-out -specs=%,$(PERL_EMBED_CCOPTS))
- PERL_EMBED_CCOPTS := $(filter-out -flto% -ffat-lto-objects, $(PERL_EMBED_CCOPTS))
- PERL_EMBED_LDOPTS := $(filter-out -specs=%,$(PERL_EMBED_LDOPTS))
- FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS)
-
- $(call feature_check,libperl)
- ifneq ($(feature-libperl), 1)
- $(error Missing perl devel files. Please install perl-ExtUtils-Embed/libperl-dev)
- else
- LDFLAGS += $(PERL_EMBED_LDFLAGS)
- EXTLIBS += $(PERL_EMBED_LIBADD)
- CFLAGS += -DHAVE_LIBPERL_SUPPORT
- $(call detected,CONFIG_LIBPERL)
- endif
-endif
+
ifeq ($(feature-timerfd), 1)
CFLAGS += -DHAVE_TIMERFD_SUPPORT
@@ -1321,7 +1302,6 @@ $(call detected_var,tipdir_SQ)
$(call detected_var,srcdir_SQ)
$(call detected_var,LIBDIR)
$(call detected_var,GTK_CFLAGS)
-$(call detected_var,PERL_EMBED_CCOPTS)
$(call detected_var,PYTHON_EMBED_CCOPTS)
ifneq ($(BISON_FILE_PREFIX_MAP),)
$(call detected_var,BISON_FILE_PREFIX_MAP)
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index cee19c923c06..7bf349198622 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -17,7 +17,7 @@ include ../scripts/utilities.mak
#
# Define CROSS_COMPILE as prefix name of compiler if you want cross-builds.
#
-# Define LIBPERL to enable perl script extension.
+
#
# Define NO_LIBPYTHON to disable python script extension.
#
@@ -1098,14 +1098,7 @@ endif
$(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
$(call QUIET_INSTALL, perf-iostat) \
$(INSTALL) $(OUTPUT)perf-iostat -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
-ifdef LIBPERL
- $(call QUIET_INSTALL, perl-scripts) \
- $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'; \
- $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -m 644 -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'; \
- $(INSTALL) scripts/perl/*.pl -m 644 -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl'; \
- $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin'; \
- $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin'
-endif
+
ifndef NO_LIBPYTHON
$(call QUIET_INSTALL, python-scripts) \
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace'; \
diff --git a/tools/perf/builtin-check.c b/tools/perf/builtin-check.c
index 3641d263b345..944038814d62 100644
--- a/tools/perf/builtin-check.c
+++ b/tools/perf/builtin-check.c
@@ -51,7 +51,7 @@ struct feature_status supported_features[] = {
FEATURE_STATUS("libLLVM", HAVE_LIBLLVM_SUPPORT),
FEATURE_STATUS("libnuma", HAVE_LIBNUMA_SUPPORT),
FEATURE_STATUS("libopencsd", HAVE_CSTRACE_SUPPORT),
- FEATURE_STATUS_TIP("libperl", HAVE_LIBPERL_SUPPORT, "Deprecated, use LIBPERL=1 and install perl-ExtUtils-Embed/libperl-dev to build with it"),
+
FEATURE_STATUS("libpfm4", HAVE_LIBPFM),
FEATURE_STATUS("libpython", HAVE_LIBPYTHON_SUPPORT),
FEATURE_STATUS("libslang", HAVE_SLANG_SUPPORT),
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index 3e3692088154..c0949556d1bb 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -2621,9 +2621,7 @@ static void process_stat_interval(u64 tstamp)
static void setup_scripting(void)
{
-#ifdef HAVE_LIBTRACEEVENT
- setup_perl_scripting();
-#endif
+
setup_python_scripting();
}
diff --git a/tools/perf/scripts/Build b/tools/perf/scripts/Build
index 91229a1fe3ff..d72cf9ad45fe 100644
--- a/tools/perf/scripts/Build
+++ b/tools/perf/scripts/Build
@@ -1,6 +1,4 @@
-ifeq ($(CONFIG_LIBTRACEEVENT),y)
- perf-util-$(CONFIG_LIBPERL) += perl/Perf-Trace-Util/
-endif
+
perf-util-$(CONFIG_LIBPYTHON) += python/Perf-Trace-Util/
ifdef MYPY
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Build b/tools/perf/scripts/perl/Perf-Trace-Util/Build
deleted file mode 100644
index 01a1a0ed51ae..000000000000
--- a/tools/perf/scripts/perl/Perf-Trace-Util/Build
+++ /dev/null
@@ -1,9 +0,0 @@
-perf-util-y += Context.o
-
-CFLAGS_Context.o += $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-bad-function-cast -Wno-declaration-after-statement -Wno-switch-enum
-CFLAGS_Context.o += -Wno-unused-parameter -Wno-nested-externs -Wno-undef
-CFLAGS_Context.o += -Wno-switch-default -Wno-shadow -Wno-thread-safety-analysis
-
-ifeq ($(CC_NO_CLANG), 1)
- CFLAGS_Context.o += -Wno-unused-command-line-argument
-endif
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Context.c b/tools/perf/scripts/perl/Perf-Trace-Util/Context.c
deleted file mode 100644
index 25c47d23a130..000000000000
--- a/tools/perf/scripts/perl/Perf-Trace-Util/Context.c
+++ /dev/null
@@ -1,122 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * This file was generated automatically by ExtUtils::ParseXS version 2.18_02 from the
- * contents of Context.xs. Do not edit this file, edit Context.xs instead.
- *
- * ANY CHANGES MADE HERE WILL BE LOST!
- */
-#include <stdbool.h>
-#ifndef HAS_BOOL
-# define HAS_BOOL 1
-#endif
-#line 1 "Context.xs"
-/*
- * Context.xs. XS interfaces for perf script.
- *
- * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com>
- */
-
-#include "EXTERN.h"
-#include "perl.h"
-#include "XSUB.h"
-#include "../../../util/trace-event.h"
-
-#ifndef PERL_UNUSED_VAR
-# define PERL_UNUSED_VAR(var) if (0) var = var
-#endif
-
-#line 42 "Context.c"
-
-XS(XS_Perf__Trace__Context_common_pc); /* prototype to pass -Wmissing-prototypes */
-XS(XS_Perf__Trace__Context_common_pc)
-{
-#ifdef dVAR
- dVAR; dXSARGS;
-#else
- dXSARGS;
-#endif
- if (items != 1)
- Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_pc", "context");
- PERL_UNUSED_VAR(cv); /* -W */
- {
- struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0)));
- int RETVAL;
- dXSTARG;
-
- RETVAL = common_pc(context);
- XSprePUSH; PUSHi((IV)RETVAL);
- }
- XSRETURN(1);
-}
-
-
-XS(XS_Perf__Trace__Context_common_flags); /* prototype to pass -Wmissing-prototypes */
-XS(XS_Perf__Trace__Context_common_flags)
-{
-#ifdef dVAR
- dVAR; dXSARGS;
-#else
- dXSARGS;
-#endif
- if (items != 1)
- Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_flags", "context");
- PERL_UNUSED_VAR(cv); /* -W */
- {
- struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0)));
- int RETVAL;
- dXSTARG;
-
- RETVAL = common_flags(context);
- XSprePUSH; PUSHi((IV)RETVAL);
- }
- XSRETURN(1);
-}
-
-
-XS(XS_Perf__Trace__Context_common_lock_depth); /* prototype to pass -Wmissing-prototypes */
-XS(XS_Perf__Trace__Context_common_lock_depth)
-{
-#ifdef dVAR
- dVAR; dXSARGS;
-#else
- dXSARGS;
-#endif
- if (items != 1)
- Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_lock_depth", "context");
- PERL_UNUSED_VAR(cv); /* -W */
- {
- struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0)));
- int RETVAL;
- dXSTARG;
-
- RETVAL = common_lock_depth(context);
- XSprePUSH; PUSHi((IV)RETVAL);
- }
- XSRETURN(1);
-}
-
-#ifdef __cplusplus
-extern "C"
-#endif
-XS(boot_Perf__Trace__Context); /* prototype to pass -Wmissing-prototypes */
-XS(boot_Perf__Trace__Context)
-{
-#ifdef dVAR
- dVAR; dXSARGS;
-#else
- dXSARGS;
-#endif
- const char* file = __FILE__;
-
- PERL_UNUSED_VAR(cv); /* -W */
- PERL_UNUSED_VAR(items); /* -W */
- XS_VERSION_BOOTCHECK ;
-
- newXSproto("Perf::Trace::Context::common_pc", XS_Perf__Trace__Context_common_pc, file, "$");
- newXSproto("Perf::Trace::Context::common_flags", XS_Perf__Trace__Context_common_flags, file, "$");
- newXSproto("Perf::Trace::Context::common_lock_depth", XS_Perf__Trace__Context_common_lock_depth, file, "$");
- if (PL_unitcheckav)
- call_list(PL_scopestack_ix, PL_unitcheckav);
- XSRETURN_YES;
-}
-
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs b/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs
deleted file mode 100644
index 8c7ea42444d1..000000000000
--- a/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Context.xs. XS interfaces for perf script.
- *
- * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include "EXTERN.h"
-#include "perl.h"
-#include "XSUB.h"
-#include "../../../perf.h"
-#include "../../../util/trace-event.h"
-
-MODULE = Perf::Trace::Context PACKAGE = Perf::Trace::Context
-PROTOTYPES: ENABLE
-
-int
-common_pc(context)
- struct scripting_context * context
-
-int
-common_flags(context)
- struct scripting_context * context
-
-int
-common_lock_depth(context)
- struct scripting_context * context
-
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Makefile.PL b/tools/perf/scripts/perl/Perf-Trace-Util/Makefile.PL
deleted file mode 100644
index e8994332d7dc..000000000000
--- a/tools/perf/scripts/perl/Perf-Trace-Util/Makefile.PL
+++ /dev/null
@@ -1,18 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-use 5.010000;
-use ExtUtils::MakeMaker;
-# See lib/ExtUtils/MakeMaker.pm for details of how to influence
-# the contents of the Makefile that is written.
-WriteMakefile(
- NAME => 'Perf::Trace::Context',
- VERSION_FROM => 'lib/Perf/Trace/Context.pm', # finds $VERSION
- PREREQ_PM => {}, # e.g., Module::Name => 1.1
- ($] >= 5.005 ? ## Add these new keywords supported since 5.005
- (ABSTRACT_FROM => 'lib/Perf/Trace/Context.pm', # retrieve abstract from module
- AUTHOR => 'Tom Zanussi <tzanussi@gmail.com>') : ()),
- LIBS => [''], # e.g., '-lm'
- DEFINE => '-I ../..', # e.g., '-DHAVE_SOMETHING'
- INC => '-I.', # e.g., '-I. -I/usr/include/other'
- # Un-comment this if you add C files to link with later:
- OBJECT => 'Context.o', # link all the C files too
-);
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/README b/tools/perf/scripts/perl/Perf-Trace-Util/README
deleted file mode 100644
index 2f0c7f3043ee..000000000000
--- a/tools/perf/scripts/perl/Perf-Trace-Util/README
+++ /dev/null
@@ -1,59 +0,0 @@
-Perf-Trace-Util version 0.01
-============================
-
-This module contains utility functions for use with perf script.
-
-Core.pm and Util.pm are pure Perl modules; Core.pm contains routines
-that the core perf support for Perl calls on and should always be
-'used', while Util.pm contains useful but optional utility functions
-that scripts may want to use. Context.pm contains the Perl->C
-interface that allows scripts to access data in the embedding perf
-executable; scripts wishing to do that should 'use Context.pm'.
-
-The Perl->C perf interface is completely driven by Context.xs. If you
-want to add new Perl functions that end up accessing C data in the
-perf executable, you add desciptions of the new functions here.
-scripting_context is a pointer to the perf data in the perf executable
-that you want to access - it's passed as the second parameter,
-$context, to all handler functions.
-
-After you do that:
-
- perl Makefile.PL # to create a Makefile for the next step
- make # to create Context.c
-
- edit Context.c to add const to the char* file = __FILE__ line in
- XS(boot_Perf__Trace__Context) to silence a warning/error.
-
- You can delete the Makefile, object files and anything else that was
- generated e.g. blib and shared library, etc, except for of course
- Context.c
-
- You should then be able to run the normal perf make as usual.
-
-INSTALLATION
-
-Building perf with perf script Perl scripting should install this
-module in the right place.
-
-You should make sure libperl and ExtUtils/Embed.pm are installed first
-e.g. apt-get install libperl-dev or yum install perl-ExtUtils-Embed.
-
-DEPENDENCIES
-
-This module requires these other modules and libraries:
-
- None
-
-COPYRIGHT AND LICENCE
-
-Copyright (C) 2009 by Tom Zanussi <tzanussi@gmail.com>
-
-This library is free software; you can redistribute it and/or modify
-it under the same terms as Perl itself, either Perl version 5.10.0 or,
-at your option, any later version of Perl 5 you may have available.
-
-Alternatively, this software may be distributed under the terms of the
-GNU General Public License ("GPL") version 2 as published by the Free
-Software Foundation.
-
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm
deleted file mode 100644
index 4e2f6039ac92..000000000000
--- a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm
+++ /dev/null
@@ -1,55 +0,0 @@
-package Perf::Trace::Context;
-
-use 5.010000;
-use strict;
-use warnings;
-
-require Exporter;
-
-our @ISA = qw(Exporter);
-
-our %EXPORT_TAGS = ( 'all' => [ qw(
-) ] );
-
-our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
-
-our @EXPORT = qw(
- common_pc common_flags common_lock_depth
-);
-
-our $VERSION = '0.01';
-
-require XSLoader;
-XSLoader::load('Perf::Trace::Context', $VERSION);
-
-1;
-__END__
-=head1 NAME
-
-Perf::Trace::Context - Perl extension for accessing functions in perf.
-
-=head1 SYNOPSIS
-
- use Perf::Trace::Context;
-
-=head1 SEE ALSO
-
-Perf (script) documentation
-
-=head1 AUTHOR
-
-Tom Zanussi, E<lt>tzanussi@gmail.com<gt>
-
-=head1 COPYRIGHT AND LICENSE
-
-Copyright (C) 2009 by Tom Zanussi
-
-This library is free software; you can redistribute it and/or modify
-it under the same terms as Perl itself, either Perl version 5.10.0 or,
-at your option, any later version of Perl 5 you may have available.
-
-Alternatively, this software may be distributed under the terms of the
-GNU General Public License ("GPL") version 2 as published by the Free
-Software Foundation.
-
-=cut
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm
deleted file mode 100644
index 9158458d3eeb..000000000000
--- a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm
+++ /dev/null
@@ -1,192 +0,0 @@
-package Perf::Trace::Core;
-
-use 5.010000;
-use strict;
-use warnings;
-
-require Exporter;
-
-our @ISA = qw(Exporter);
-
-our %EXPORT_TAGS = ( 'all' => [ qw(
-) ] );
-
-our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
-
-our @EXPORT = qw(
-define_flag_field define_flag_value flag_str dump_flag_fields
-define_symbolic_field define_symbolic_value symbol_str dump_symbolic_fields
-trace_flag_str
-);
-
-our $VERSION = '0.01';
-
-my %trace_flags = (0x00 => "NONE",
- 0x01 => "IRQS_OFF",
- 0x02 => "IRQS_NOSUPPORT",
- 0x04 => "NEED_RESCHED",
- 0x08 => "HARDIRQ",
- 0x10 => "SOFTIRQ");
-
-sub trace_flag_str
-{
- my ($value) = @_;
-
- my $string;
-
- my $print_delim = 0;
-
- foreach my $idx (sort {$a <=> $b} keys %trace_flags) {
- if (!$value && !$idx) {
- $string .= "NONE";
- last;
- }
-
- if ($idx && ($value & $idx) == $idx) {
- if ($print_delim) {
- $string .= " | ";
- }
- $string .= "$trace_flags{$idx}";
- $print_delim = 1;
- $value &= ~$idx;
- }
- }
-
- return $string;
-}
-
-my %flag_fields;
-my %symbolic_fields;
-
-sub flag_str
-{
- my ($event_name, $field_name, $value) = @_;
-
- my $string;
-
- if ($flag_fields{$event_name}{$field_name}) {
- my $print_delim = 0;
- foreach my $idx (sort {$a <=> $b} keys %{$flag_fields{$event_name}{$field_name}{"values"}}) {
- if (!$value && !$idx) {
- $string .= "$flag_fields{$event_name}{$field_name}{'values'}{$idx}";
- last;
- }
- if ($idx && ($value & $idx) == $idx) {
- if ($print_delim && $flag_fields{$event_name}{$field_name}{'delim'}) {
- $string .= " $flag_fields{$event_name}{$field_name}{'delim'} ";
- }
- $string .= "$flag_fields{$event_name}{$field_name}{'values'}{$idx}";
- $print_delim = 1;
- $value &= ~$idx;
- }
- }
- }
-
- return $string;
-}
-
-sub define_flag_field
-{
- my ($event_name, $field_name, $delim) = @_;
-
- $flag_fields{$event_name}{$field_name}{"delim"} = $delim;
-}
-
-sub define_flag_value
-{
- my ($event_name, $field_name, $value, $field_str) = @_;
-
- $flag_fields{$event_name}{$field_name}{"values"}{$value} = $field_str;
-}
-
-sub dump_flag_fields
-{
- for my $event (keys %flag_fields) {
- print "event $event:\n";
- for my $field (keys %{$flag_fields{$event}}) {
- print " field: $field:\n";
- print " delim: $flag_fields{$event}{$field}{'delim'}\n";
- foreach my $idx (sort {$a <=> $b} keys %{$flag_fields{$event}{$field}{"values"}}) {
- print " value $idx: $flag_fields{$event}{$field}{'values'}{$idx}\n";
- }
- }
- }
-}
-
-sub symbol_str
-{
- my ($event_name, $field_name, $value) = @_;
-
- if ($symbolic_fields{$event_name}{$field_name}) {
- foreach my $idx (sort {$a <=> $b} keys %{$symbolic_fields{$event_name}{$field_name}{"values"}}) {
- if (!$value && !$idx) {
- return "$symbolic_fields{$event_name}{$field_name}{'values'}{$idx}";
- last;
- }
- if ($value == $idx) {
- return "$symbolic_fields{$event_name}{$field_name}{'values'}{$idx}";
- }
- }
- }
-
- return undef;
-}
-
-sub define_symbolic_field
-{
- my ($event_name, $field_name) = @_;
-
- # nothing to do, really
-}
-
-sub define_symbolic_value
-{
- my ($event_name, $field_name, $value, $field_str) = @_;
-
- $symbolic_fields{$event_name}{$field_name}{"values"}{$value} = $field_str;
-}
-
-sub dump_symbolic_fields
-{
- for my $event (keys %symbolic_fields) {
- print "event $event:\n";
- for my $field (keys %{$symbolic_fields{$event}}) {
- print " field: $field:\n";
- foreach my $idx (sort {$a <=> $b} keys %{$symbolic_fields{$event}{$field}{"values"}}) {
- print " value $idx: $symbolic_fields{$event}{$field}{'values'}{$idx}\n";
- }
- }
- }
-}
-
-1;
-__END__
-=head1 NAME
-
-Perf::Trace::Core - Perl extension for perf script
-
-=head1 SYNOPSIS
-
- use Perf::Trace::Core
-
-=head1 SEE ALSO
-
-Perf (script) documentation
-
-=head1 AUTHOR
-
-Tom Zanussi, E<lt>tzanussi@gmail.com<gt>
-
-=head1 COPYRIGHT AND LICENSE
-
-Copyright (C) 2009 by Tom Zanussi
-
-This library is free software; you can redistribute it and/or modify
-it under the same terms as Perl itself, either Perl version 5.10.0 or,
-at your option, any later version of Perl 5 you may have available.
-
-Alternatively, this software may be distributed under the terms of the
-GNU General Public License ("GPL") version 2 as published by the Free
-Software Foundation.
-
-=cut
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm
deleted file mode 100644
index 053500114625..000000000000
--- a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm
+++ /dev/null
@@ -1,94 +0,0 @@
-package Perf::Trace::Util;
-
-use 5.010000;
-use strict;
-use warnings;
-
-require Exporter;
-
-our @ISA = qw(Exporter);
-
-our %EXPORT_TAGS = ( 'all' => [ qw(
-) ] );
-
-our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
-
-our @EXPORT = qw(
-avg nsecs nsecs_secs nsecs_nsecs nsecs_usecs print_nsecs
-clear_term
-);
-
-our $VERSION = '0.01';
-
-sub avg
-{
- my ($total, $n) = @_;
-
- return $total / $n;
-}
-
-my $NSECS_PER_SEC = 1000000000;
-
-sub nsecs
-{
- my ($secs, $nsecs) = @_;
-
- return $secs * $NSECS_PER_SEC + $nsecs;
-}
-
-sub nsecs_secs {
- my ($nsecs) = @_;
-
- return $nsecs / $NSECS_PER_SEC;
-}
-
-sub nsecs_nsecs {
- my ($nsecs) = @_;
-
- return $nsecs % $NSECS_PER_SEC;
-}
-
-sub nsecs_str {
- my ($nsecs) = @_;
-
- my $str = sprintf("%5u.%09u", nsecs_secs($nsecs), nsecs_nsecs($nsecs));
-
- return $str;
-}
-
-sub clear_term
-{
- print "\x1b[H\x1b[2J";
-}
-
-1;
-__END__
-=head1 NAME
-
-Perf::Trace::Util - Perl extension for perf script
-
-=head1 SYNOPSIS
-
- use Perf::Trace::Util;
-
-=head1 SEE ALSO
-
-Perf (script) documentation
-
-=head1 AUTHOR
-
-Tom Zanussi, E<lt>tzanussi@gmail.com<gt>
-
-=head1 COPYRIGHT AND LICENSE
-
-Copyright (C) 2009 by Tom Zanussi
-
-This library is free software; you can redistribute it and/or modify
-it under the same terms as Perl itself, either Perl version 5.10.0 or,
-at your option, any later version of Perl 5 you may have available.
-
-Alternatively, this software may be distributed under the terms of the
-GNU General Public License ("GPL") version 2 as published by the Free
-Software Foundation.
-
-=cut
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/typemap b/tools/perf/scripts/perl/Perf-Trace-Util/typemap
deleted file mode 100644
index 840836804aa7..000000000000
--- a/tools/perf/scripts/perl/Perf-Trace-Util/typemap
+++ /dev/null
@@ -1 +0,0 @@
-struct scripting_context * T_PTR
diff --git a/tools/perf/scripts/perl/bin/check-perf-trace-record b/tools/perf/scripts/perl/bin/check-perf-trace-record
deleted file mode 100644
index 423ad6aed056..000000000000
--- a/tools/perf/scripts/perl/bin/check-perf-trace-record
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-perf record -a -e kmem:kmalloc -e irq:softirq_entry -e kmem:kfree
diff --git a/tools/perf/scripts/perl/bin/failed-syscalls-record b/tools/perf/scripts/perl/bin/failed-syscalls-record
deleted file mode 100644
index 74685f318379..000000000000
--- a/tools/perf/scripts/perl/bin/failed-syscalls-record
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-(perf record -e raw_syscalls:sys_exit $@ || \
- perf record -e syscalls:sys_exit $@) 2> /dev/null
diff --git a/tools/perf/scripts/perl/bin/failed-syscalls-report b/tools/perf/scripts/perl/bin/failed-syscalls-report
deleted file mode 100644
index 9f83cc1ad8ba..000000000000
--- a/tools/perf/scripts/perl/bin/failed-syscalls-report
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-# description: system-wide failed syscalls
-# args: [comm]
-if [ $# -gt 0 ] ; then
- if ! expr match "$1" "-" > /dev/null ; then
- comm=$1
- shift
- fi
-fi
-perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/failed-syscalls.pl $comm
diff --git a/tools/perf/scripts/perl/bin/rw-by-file-record b/tools/perf/scripts/perl/bin/rw-by-file-record
deleted file mode 100644
index 33efc8673aae..000000000000
--- a/tools/perf/scripts/perl/bin/rw-by-file-record
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-perf record -e syscalls:sys_enter_read -e syscalls:sys_enter_write $@
-
diff --git a/tools/perf/scripts/perl/bin/rw-by-file-report b/tools/perf/scripts/perl/bin/rw-by-file-report
deleted file mode 100644
index 77200b3f3100..000000000000
--- a/tools/perf/scripts/perl/bin/rw-by-file-report
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-# description: r/w activity for a program, by file
-# args: <comm>
-if [ $# -lt 1 ] ; then
- echo "usage: rw-by-file <comm>"
- exit
-fi
-comm=$1
-shift
-perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/rw-by-file.pl $comm
diff --git a/tools/perf/scripts/perl/bin/rw-by-pid-record b/tools/perf/scripts/perl/bin/rw-by-pid-record
deleted file mode 100644
index 7cb9db230448..000000000000
--- a/tools/perf/scripts/perl/bin/rw-by-pid-record
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-perf record -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@
diff --git a/tools/perf/scripts/perl/bin/rw-by-pid-report b/tools/perf/scripts/perl/bin/rw-by-pid-report
deleted file mode 100644
index a27b9f311f95..000000000000
--- a/tools/perf/scripts/perl/bin/rw-by-pid-report
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-# description: system-wide r/w activity
-perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/rw-by-pid.pl
diff --git a/tools/perf/scripts/perl/bin/rwtop-record b/tools/perf/scripts/perl/bin/rwtop-record
deleted file mode 100644
index 7cb9db230448..000000000000
--- a/tools/perf/scripts/perl/bin/rwtop-record
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-perf record -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@
diff --git a/tools/perf/scripts/perl/bin/rwtop-report b/tools/perf/scripts/perl/bin/rwtop-report
deleted file mode 100644
index 83e11ec2e190..000000000000
--- a/tools/perf/scripts/perl/bin/rwtop-report
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-# description: system-wide r/w top
-# args: [interval]
-n_args=0
-for i in "$@"
-do
- if expr match "$i" "-" > /dev/null ; then
- break
- fi
- n_args=$(( $n_args + 1 ))
-done
-if [ "$n_args" -gt 1 ] ; then
- echo "usage: rwtop-report [interval]"
- exit
-fi
-if [ "$n_args" -gt 0 ] ; then
- interval=$1
- shift
-fi
-perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/rwtop.pl $interval
diff --git a/tools/perf/scripts/perl/bin/wakeup-latency-record b/tools/perf/scripts/perl/bin/wakeup-latency-record
deleted file mode 100644
index 464251a1bd7e..000000000000
--- a/tools/perf/scripts/perl/bin/wakeup-latency-record
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/bash
-perf record -e sched:sched_switch -e sched:sched_wakeup $@
-
-
-
-
diff --git a/tools/perf/scripts/perl/bin/wakeup-latency-report b/tools/perf/scripts/perl/bin/wakeup-latency-report
deleted file mode 100644
index 889e8130cca5..000000000000
--- a/tools/perf/scripts/perl/bin/wakeup-latency-report
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-# description: system-wide min/max/avg wakeup latency
-perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/wakeup-latency.pl
diff --git a/tools/perf/scripts/perl/check-perf-trace.pl b/tools/perf/scripts/perl/check-perf-trace.pl
deleted file mode 100644
index d307ce8fd6ed..000000000000
--- a/tools/perf/scripts/perl/check-perf-trace.pl
+++ /dev/null
@@ -1,106 +0,0 @@
-# perf script event handlers, generated by perf script -g perl
-# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
-# Licensed under the terms of the GNU GPL License version 2
-
-# This script tests basic functionality such as flag and symbol
-# strings, common_xxx() calls back into perf, begin, end, unhandled
-# events, etc. Basically, if this script runs successfully and
-# displays expected results, perl scripting support should be ok.
-
-use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
-use lib "./Perf-Trace-Util/lib";
-use Perf::Trace::Core;
-use Perf::Trace::Context;
-use Perf::Trace::Util;
-
-sub trace_begin
-{
- print "trace_begin\n";
-}
-
-sub trace_end
-{
- print "trace_end\n";
-
- print_unhandled();
-}
-
-sub irq::softirq_entry
-{
- my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm, $common_callchain,
- $vec) = @_;
-
- print_header($event_name, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm);
-
- print_uncommon($context);
-
- printf("vec=%s\n",
- symbol_str("irq::softirq_entry", "vec", $vec));
-}
-
-sub kmem::kmalloc
-{
- my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm, $common_callchain,
- $call_site, $ptr, $bytes_req, $bytes_alloc,
- $gfp_flags) = @_;
-
- print_header($event_name, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm);
-
- print_uncommon($context);
-
- printf("call_site=%p, ptr=%p, bytes_req=%u, bytes_alloc=%u, ".
- "gfp_flags=%s\n",
- $call_site, $ptr, $bytes_req, $bytes_alloc,
-
- flag_str("kmem::kmalloc", "gfp_flags", $gfp_flags));
-}
-
-# print trace fields not included in handler args
-sub print_uncommon
-{
- my ($context) = @_;
-
- printf("common_preempt_count=%d, common_flags=%s, common_lock_depth=%d, ",
- common_pc($context), trace_flag_str(common_flags($context)),
- common_lock_depth($context));
-
-}
-
-my %unhandled;
-
-sub print_unhandled
-{
- if ((scalar keys %unhandled) == 0) {
- return;
- }
-
- print "\nunhandled events:\n\n";
-
- printf("%-40s %10s\n", "event", "count");
- printf("%-40s %10s\n", "----------------------------------------",
- "-----------");
-
- foreach my $event_name (keys %unhandled) {
- printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
- }
-}
-
-sub trace_unhandled
-{
- my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm, $common_callchain) = @_;
-
- $unhandled{$event_name}++;
-}
-
-sub print_header
-{
- my ($event_name, $cpu, $secs, $nsecs, $pid, $comm) = @_;
-
- printf("%-20s %5u %05u.%09u %8u %-20s ",
- $event_name, $cpu, $secs, $nsecs, $pid, $comm);
-}
diff --git a/tools/perf/scripts/perl/failed-syscalls.pl b/tools/perf/scripts/perl/failed-syscalls.pl
deleted file mode 100644
index 05954a8f363a..000000000000
--- a/tools/perf/scripts/perl/failed-syscalls.pl
+++ /dev/null
@@ -1,47 +0,0 @@
-# failed system call counts
-# (c) 2010, Tom Zanussi <tzanussi@gmail.com>
-# Licensed under the terms of the GNU GPL License version 2
-#
-# Displays system-wide failed system call totals
-# If a [comm] arg is specified, only syscalls called by [comm] are displayed.
-
-use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
-use lib "./Perf-Trace-Util/lib";
-use Perf::Trace::Core;
-use Perf::Trace::Context;
-use Perf::Trace::Util;
-
-my $for_comm = shift;
-
-my %failed_syscalls;
-
-sub raw_syscalls::sys_exit
-{
- my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm, $common_callchain,
- $id, $ret) = @_;
-
- if ($ret < 0) {
- $failed_syscalls{$common_comm}++;
- }
-}
-
-sub syscalls::sys_exit
-{
- raw_syscalls::sys_exit(@_)
-}
-
-sub trace_end
-{
- printf("\nfailed syscalls by comm:\n\n");
-
- printf("%-20s %10s\n", "comm", "# errors");
- printf("%-20s %6s %10s\n", "--------------------", "----------");
-
- foreach my $comm (sort {$failed_syscalls{$b} <=> $failed_syscalls{$a}}
- keys %failed_syscalls) {
- next if ($for_comm && $comm ne $for_comm);
-
- printf("%-20s %10s\n", $comm, $failed_syscalls{$comm});
- }
-}
diff --git a/tools/perf/scripts/perl/rw-by-file.pl b/tools/perf/scripts/perl/rw-by-file.pl
deleted file mode 100644
index 92a750b8552b..000000000000
--- a/tools/perf/scripts/perl/rw-by-file.pl
+++ /dev/null
@@ -1,106 +0,0 @@
-#!/usr/bin/perl -w
-# SPDX-License-Identifier: GPL-2.0-only
-# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
-
-# Display r/w activity for files read/written to for a given program
-
-# The common_* event handler fields are the most useful fields common to
-# all events. They don't necessarily correspond to the 'common_*' fields
-# in the status files. Those fields not available as handler params can
-# be retrieved via script functions of the form get_common_*().
-
-use 5.010000;
-use strict;
-use warnings;
-
-use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
-use lib "./Perf-Trace-Util/lib";
-use Perf::Trace::Core;
-use Perf::Trace::Util;
-
-my $usage = "perf script -s rw-by-file.pl <comm>\n";
-
-my $for_comm = shift or die $usage;
-
-my %reads;
-my %writes;
-
-sub syscalls::sys_enter_read
-{
- my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm, $common_callchain, $nr, $fd, $buf, $count) = @_;
-
- if ($common_comm eq $for_comm) {
- $reads{$fd}{bytes_requested} += $count;
- $reads{$fd}{total_reads}++;
- }
-}
-
-sub syscalls::sys_enter_write
-{
- my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm, $common_callchain, $nr, $fd, $buf, $count) = @_;
-
- if ($common_comm eq $for_comm) {
- $writes{$fd}{bytes_written} += $count;
- $writes{$fd}{total_writes}++;
- }
-}
-
-sub trace_end
-{
- printf("file read counts for $for_comm:\n\n");
-
- printf("%6s %10s %10s\n", "fd", "# reads", "bytes_requested");
- printf("%6s %10s %10s\n", "------", "----------", "-----------");
-
- foreach my $fd (sort {$reads{$b}{bytes_requested} <=>
- $reads{$a}{bytes_requested}} keys %reads) {
- my $total_reads = $reads{$fd}{total_reads};
- my $bytes_requested = $reads{$fd}{bytes_requested};
- printf("%6u %10u %10u\n", $fd, $total_reads, $bytes_requested);
- }
-
- printf("\nfile write counts for $for_comm:\n\n");
-
- printf("%6s %10s %10s\n", "fd", "# writes", "bytes_written");
- printf("%6s %10s %10s\n", "------", "----------", "-----------");
-
- foreach my $fd (sort {$writes{$b}{bytes_written} <=>
- $writes{$a}{bytes_written}} keys %writes) {
- my $total_writes = $writes{$fd}{total_writes};
- my $bytes_written = $writes{$fd}{bytes_written};
- printf("%6u %10u %10u\n", $fd, $total_writes, $bytes_written);
- }
-
- print_unhandled();
-}
-
-my %unhandled;
-
-sub print_unhandled
-{
- if ((scalar keys %unhandled) == 0) {
- return;
- }
-
- print "\nunhandled events:\n\n";
-
- printf("%-40s %10s\n", "event", "count");
- printf("%-40s %10s\n", "----------------------------------------",
- "-----------");
-
- foreach my $event_name (keys %unhandled) {
- printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
- }
-}
-
-sub trace_unhandled
-{
- my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm, $common_callchain) = @_;
-
- $unhandled{$event_name}++;
-}
-
-
diff --git a/tools/perf/scripts/perl/rw-by-pid.pl b/tools/perf/scripts/perl/rw-by-pid.pl
deleted file mode 100644
index d789fe39caab..000000000000
--- a/tools/perf/scripts/perl/rw-by-pid.pl
+++ /dev/null
@@ -1,184 +0,0 @@
-#!/usr/bin/perl -w
-# SPDX-License-Identifier: GPL-2.0-only
-# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
-
-# Display r/w activity for all processes
-
-# The common_* event handler fields are the most useful fields common to
-# all events. They don't necessarily correspond to the 'common_*' fields
-# in the status files. Those fields not available as handler params can
-# be retrieved via script functions of the form get_common_*().
-
-use 5.010000;
-use strict;
-use warnings;
-
-use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
-use lib "./Perf-Trace-Util/lib";
-use Perf::Trace::Core;
-use Perf::Trace::Util;
-
-my %reads;
-my %writes;
-
-sub syscalls::sys_exit_read
-{
- my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm, $common_callchain,
- $nr, $ret) = @_;
-
- if ($ret > 0) {
- $reads{$common_pid}{bytes_read} += $ret;
- } else {
- if (!defined ($reads{$common_pid}{bytes_read})) {
- $reads{$common_pid}{bytes_read} = 0;
- }
- $reads{$common_pid}{errors}{$ret}++;
- }
-}
-
-sub syscalls::sys_enter_read
-{
- my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm, $common_callchain,
- $nr, $fd, $buf, $count) = @_;
-
- $reads{$common_pid}{bytes_requested} += $count;
- $reads{$common_pid}{total_reads}++;
- $reads{$common_pid}{comm} = $common_comm;
-}
-
-sub syscalls::sys_exit_write
-{
- my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm, $common_callchain,
- $nr, $ret) = @_;
-
- if ($ret <= 0) {
- $writes{$common_pid}{errors}{$ret}++;
- }
-}
-
-sub syscalls::sys_enter_write
-{
- my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm, $common_callchain,
- $nr, $fd, $buf, $count) = @_;
-
- $writes{$common_pid}{bytes_written} += $count;
- $writes{$common_pid}{total_writes}++;
- $writes{$common_pid}{comm} = $common_comm;
-}
-
-sub trace_end
-{
- printf("read counts by pid:\n\n");
-
- printf("%6s %20s %10s %10s %10s\n", "pid", "comm",
- "# reads", "bytes_requested", "bytes_read");
- printf("%6s %-20s %10s %10s %10s\n", "------", "--------------------",
- "-----------", "----------", "----------");
-
- foreach my $pid (sort { ($reads{$b}{bytes_read} || 0) <=>
- ($reads{$a}{bytes_read} || 0) } keys %reads) {
- my $comm = $reads{$pid}{comm} || "";
- my $total_reads = $reads{$pid}{total_reads} || 0;
- my $bytes_requested = $reads{$pid}{bytes_requested} || 0;
- my $bytes_read = $reads{$pid}{bytes_read} || 0;
-
- printf("%6s %-20s %10s %10s %10s\n", $pid, $comm,
- $total_reads, $bytes_requested, $bytes_read);
- }
-
- printf("\nfailed reads by pid:\n\n");
-
- printf("%6s %20s %6s %10s\n", "pid", "comm", "error #", "# errors");
- printf("%6s %20s %6s %10s\n", "------", "--------------------",
- "------", "----------");
-
- my @errcounts = ();
-
- foreach my $pid (keys %reads) {
- foreach my $error (keys %{$reads{$pid}{errors}}) {
- my $comm = $reads{$pid}{comm} || "";
- my $errcount = $reads{$pid}{errors}{$error} || 0;
- push @errcounts, [$pid, $comm, $error, $errcount];
- }
- }
-
- @errcounts = sort { $b->[3] <=> $a->[3] } @errcounts;
-
- for my $i (0 .. $#errcounts) {
- printf("%6d %-20s %6d %10s\n", $errcounts[$i][0],
- $errcounts[$i][1], $errcounts[$i][2], $errcounts[$i][3]);
- }
-
- printf("\nwrite counts by pid:\n\n");
-
- printf("%6s %20s %10s %10s\n", "pid", "comm",
- "# writes", "bytes_written");
- printf("%6s %-20s %10s %10s\n", "------", "--------------------",
- "-----------", "----------");
-
- foreach my $pid (sort { ($writes{$b}{bytes_written} || 0) <=>
- ($writes{$a}{bytes_written} || 0)} keys %writes) {
- my $comm = $writes{$pid}{comm} || "";
- my $total_writes = $writes{$pid}{total_writes} || 0;
- my $bytes_written = $writes{$pid}{bytes_written} || 0;
-
- printf("%6s %-20s %10s %10s\n", $pid, $comm,
- $total_writes, $bytes_written);
- }
-
- printf("\nfailed writes by pid:\n\n");
-
- printf("%6s %20s %6s %10s\n", "pid", "comm", "error #", "# errors");
- printf("%6s %20s %6s %10s\n", "------", "--------------------",
- "------", "----------");
-
- @errcounts = ();
-
- foreach my $pid (keys %writes) {
- foreach my $error (keys %{$writes{$pid}{errors}}) {
- my $comm = $writes{$pid}{comm} || "";
- my $errcount = $writes{$pid}{errors}{$error} || 0;
- push @errcounts, [$pid, $comm, $error, $errcount];
- }
- }
-
- @errcounts = sort { $b->[3] <=> $a->[3] } @errcounts;
-
- for my $i (0 .. $#errcounts) {
- printf("%6d %-20s %6d %10s\n", $errcounts[$i][0],
- $errcounts[$i][1], $errcounts[$i][2], $errcounts[$i][3]);
- }
-
- print_unhandled();
-}
-
-my %unhandled;
-
-sub print_unhandled
-{
- if ((scalar keys %unhandled) == 0) {
- return;
- }
-
- print "\nunhandled events:\n\n";
-
- printf("%-40s %10s\n", "event", "count");
- printf("%-40s %10s\n", "----------------------------------------",
- "-----------");
-
- foreach my $event_name (keys %unhandled) {
- printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
- }
-}
-
-sub trace_unhandled
-{
- my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm, $common_callchain) = @_;
-
- $unhandled{$event_name}++;
-}
diff --git a/tools/perf/scripts/perl/rwtop.pl b/tools/perf/scripts/perl/rwtop.pl
deleted file mode 100644
index eba4df67af6b..000000000000
--- a/tools/perf/scripts/perl/rwtop.pl
+++ /dev/null
@@ -1,203 +0,0 @@
-#!/usr/bin/perl -w
-# SPDX-License-Identifier: GPL-2.0-only
-# (c) 2010, Tom Zanussi <tzanussi@gmail.com>
-
-# read/write top
-#
-# Periodically displays system-wide r/w call activity, broken down by
-# pid. If an [interval] arg is specified, the display will be
-# refreshed every [interval] seconds. The default interval is 3
-# seconds.
-
-use 5.010000;
-use strict;
-use warnings;
-
-use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
-use lib "./Perf-Trace-Util/lib";
-use Perf::Trace::Core;
-use Perf::Trace::Util;
-use POSIX qw/SIGALRM SA_RESTART/;
-
-my $default_interval = 3;
-my $nlines = 20;
-my $print_thread;
-my $print_pending = 0;
-
-my %reads;
-my %writes;
-
-my $interval = shift;
-if (!$interval) {
- $interval = $default_interval;
-}
-
-sub syscalls::sys_exit_read
-{
- my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm, $common_callchain,
- $nr, $ret) = @_;
-
- print_check();
-
- if ($ret > 0) {
- $reads{$common_pid}{bytes_read} += $ret;
- } else {
- if (!defined ($reads{$common_pid}{bytes_read})) {
- $reads{$common_pid}{bytes_read} = 0;
- }
- $reads{$common_pid}{errors}{$ret}++;
- }
-}
-
-sub syscalls::sys_enter_read
-{
- my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm, $common_callchain,
- $nr, $fd, $buf, $count) = @_;
-
- print_check();
-
- $reads{$common_pid}{bytes_requested} += $count;
- $reads{$common_pid}{total_reads}++;
- $reads{$common_pid}{comm} = $common_comm;
-}
-
-sub syscalls::sys_exit_write
-{
- my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm, $common_callchain,
- $nr, $ret) = @_;
-
- print_check();
-
- if ($ret <= 0) {
- $writes{$common_pid}{errors}{$ret}++;
- }
-}
-
-sub syscalls::sys_enter_write
-{
- my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm, $common_callchain,
- $nr, $fd, $buf, $count) = @_;
-
- print_check();
-
- $writes{$common_pid}{bytes_written} += $count;
- $writes{$common_pid}{total_writes}++;
- $writes{$common_pid}{comm} = $common_comm;
-}
-
-sub trace_begin
-{
- my $sa = POSIX::SigAction->new(\&set_print_pending);
- $sa->flags(SA_RESTART);
- $sa->safe(1);
- POSIX::sigaction(SIGALRM, $sa) or die "Can't set SIGALRM handler: $!\n";
- alarm 1;
-}
-
-sub trace_end
-{
- print_unhandled();
- print_totals();
-}
-
-sub print_check()
-{
- if ($print_pending == 1) {
- $print_pending = 0;
- print_totals();
- }
-}
-
-sub set_print_pending()
-{
- $print_pending = 1;
- alarm $interval;
-}
-
-sub print_totals
-{
- my $count;
-
- $count = 0;
-
- clear_term();
-
- printf("\nread counts by pid:\n\n");
-
- printf("%6s %20s %10s %10s %10s\n", "pid", "comm",
- "# reads", "bytes_req", "bytes_read");
- printf("%6s %-20s %10s %10s %10s\n", "------", "--------------------",
- "----------", "----------", "----------");
-
- foreach my $pid (sort { ($reads{$b}{bytes_read} || 0) <=>
- ($reads{$a}{bytes_read} || 0) } keys %reads) {
- my $comm = $reads{$pid}{comm} || "";
- my $total_reads = $reads{$pid}{total_reads} || 0;
- my $bytes_requested = $reads{$pid}{bytes_requested} || 0;
- my $bytes_read = $reads{$pid}{bytes_read} || 0;
-
- printf("%6s %-20s %10s %10s %10s\n", $pid, $comm,
- $total_reads, $bytes_requested, $bytes_read);
-
- if (++$count == $nlines) {
- last;
- }
- }
-
- $count = 0;
-
- printf("\nwrite counts by pid:\n\n");
-
- printf("%6s %20s %10s %13s\n", "pid", "comm",
- "# writes", "bytes_written");
- printf("%6s %-20s %10s %13s\n", "------", "--------------------",
- "----------", "-------------");
-
- foreach my $pid (sort { ($writes{$b}{bytes_written} || 0) <=>
- ($writes{$a}{bytes_written} || 0)} keys %writes) {
- my $comm = $writes{$pid}{comm} || "";
- my $total_writes = $writes{$pid}{total_writes} || 0;
- my $bytes_written = $writes{$pid}{bytes_written} || 0;
-
- printf("%6s %-20s %10s %13s\n", $pid, $comm,
- $total_writes, $bytes_written);
-
- if (++$count == $nlines) {
- last;
- }
- }
-
- %reads = ();
- %writes = ();
-}
-
-my %unhandled;
-
-sub print_unhandled
-{
- if ((scalar keys %unhandled) == 0) {
- return;
- }
-
- print "\nunhandled events:\n\n";
-
- printf("%-40s %10s\n", "event", "count");
- printf("%-40s %10s\n", "----------------------------------------",
- "-----------");
-
- foreach my $event_name (keys %unhandled) {
- printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
- }
-}
-
-sub trace_unhandled
-{
- my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm, $common_callchain) = @_;
-
- $unhandled{$event_name}++;
-}
diff --git a/tools/perf/scripts/perl/wakeup-latency.pl b/tools/perf/scripts/perl/wakeup-latency.pl
deleted file mode 100644
index 53444ff4ec7f..000000000000
--- a/tools/perf/scripts/perl/wakeup-latency.pl
+++ /dev/null
@@ -1,107 +0,0 @@
-#!/usr/bin/perl -w
-# SPDX-License-Identifier: GPL-2.0-only
-# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
-
-# Display avg/min/max wakeup latency
-
-# The common_* event handler fields are the most useful fields common to
-# all events. They don't necessarily correspond to the 'common_*' fields
-# in the status files. Those fields not available as handler params can
-# be retrieved via script functions of the form get_common_*().
-
-use 5.010000;
-use strict;
-use warnings;
-
-use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
-use lib "./Perf-Trace-Util/lib";
-use Perf::Trace::Core;
-use Perf::Trace::Util;
-
-my %last_wakeup;
-
-my $max_wakeup_latency;
-my $min_wakeup_latency;
-my $total_wakeup_latency = 0;
-my $total_wakeups = 0;
-
-sub sched::sched_switch
-{
- my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm, $common_callchain,
- $prev_comm, $prev_pid, $prev_prio, $prev_state, $next_comm, $next_pid,
- $next_prio) = @_;
-
- my $wakeup_ts = $last_wakeup{$common_cpu}{ts};
- if ($wakeup_ts) {
- my $switch_ts = nsecs($common_secs, $common_nsecs);
- my $wakeup_latency = $switch_ts - $wakeup_ts;
- if ($wakeup_latency > $max_wakeup_latency) {
- $max_wakeup_latency = $wakeup_latency;
- }
- if ($wakeup_latency < $min_wakeup_latency) {
- $min_wakeup_latency = $wakeup_latency;
- }
- $total_wakeup_latency += $wakeup_latency;
- $total_wakeups++;
- }
- $last_wakeup{$common_cpu}{ts} = 0;
-}
-
-sub sched::sched_wakeup
-{
- my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm, $common_callchain,
- $comm, $pid, $prio, $success, $target_cpu) = @_;
-
- $last_wakeup{$target_cpu}{ts} = nsecs($common_secs, $common_nsecs);
-}
-
-sub trace_begin
-{
- $min_wakeup_latency = 1000000000;
- $max_wakeup_latency = 0;
-}
-
-sub trace_end
-{
- printf("wakeup_latency stats:\n\n");
- print "total_wakeups: $total_wakeups\n";
- if ($total_wakeups) {
- printf("avg_wakeup_latency (ns): %u\n",
- avg($total_wakeup_latency, $total_wakeups));
- } else {
- printf("avg_wakeup_latency (ns): N/A\n");
- }
- printf("min_wakeup_latency (ns): %u\n", $min_wakeup_latency);
- printf("max_wakeup_latency (ns): %u\n", $max_wakeup_latency);
-
- print_unhandled();
-}
-
-my %unhandled;
-
-sub print_unhandled
-{
- if ((scalar keys %unhandled) == 0) {
- return;
- }
-
- print "\nunhandled events:\n\n";
-
- printf("%-40s %10s\n", "event", "count");
- printf("%-40s %10s\n", "----------------------------------------",
- "-----------");
-
- foreach my $event_name (keys %unhandled) {
- printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
- }
-}
-
-sub trace_unhandled
-{
- my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
- $common_pid, $common_comm, $common_callchain) = @_;
-
- $unhandled{$event_name}++;
-}
diff --git a/tools/perf/tests/make b/tools/perf/tests/make
index 6587dc326d1b..31b064928cfc 100644
--- a/tools/perf/tests/make
+++ b/tools/perf/tests/make
@@ -74,7 +74,7 @@ make_no_jevents := NO_JEVENTS=1
make_jevents_all := JEVENTS_ARCH=all
make_no_bpf_skel := BUILD_BPF_SKEL=0
make_gen_vmlinux_h := GEN_VMLINUX_H=1
-make_libperl := LIBPERL=1
+
make_no_libpython := NO_LIBPYTHON=1
make_no_scripts := NO_LIBPYTHON=1
make_no_slang := NO_SLANG=1
@@ -149,7 +149,7 @@ run += make_no_jevents
run += make_jevents_all
run += make_no_bpf_skel
run += make_gen_vmlinux_h
-run += make_libperl
+
run += make_no_libpython
run += make_no_scripts
run += make_no_slang
diff --git a/tools/perf/tests/shell/script_perl.sh b/tools/perf/tests/shell/script_perl.sh
deleted file mode 100755
index b6d65b6fbda1..000000000000
--- a/tools/perf/tests/shell/script_perl.sh
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/bin/bash
-# perf script perl tests
-# SPDX-License-Identifier: GPL-2.0
-
-set -e
-
-# set PERF_EXEC_PATH to find scripts in the source directory
-perfdir=$(dirname "$0")/../..
-if [ -e "$perfdir/scripts/perl/Perf-Trace-Util" ]; then
- export PERF_EXEC_PATH=$perfdir
-fi
-
-
-perfdata=$(mktemp /tmp/__perf_test_script_perl.perf.data.XXXXX)
-generated_script=$(mktemp /tmp/__perf_test_script.XXXXX.pl)
-
-cleanup() {
- rm -f "${perfdata}"
- rm -f "${generated_script}"
- trap - EXIT TERM INT
-}
-
-trap_cleanup() {
- echo "Unexpected signal in ${FUNCNAME[1]}"
- cleanup
- exit 1
-}
-trap trap_cleanup TERM INT
-trap cleanup EXIT
-
-check_perl_support() {
- if perf check feature -q libperl; then
- return 0
- fi
- echo "perf script perl test [Skipped: no libperl support]"
- return 2
-}
-
-test_script() {
- local event_name=$1
- local expected_output=$2
- local record_opts=$3
-
- echo "Testing event: $event_name"
-
- # Try to record. If this fails, it might be permissions or lack of support.
- # We return 2 to indicate "skip this event" rather than "fail test".
- if ! perf record -o "${perfdata}" -e "$event_name" $record_opts -- perf test -w thloop > /dev/null 2>&1; then
- echo "perf script perl test [Skipped: failed to record $event_name]"
- return 2
- fi
-
- echo "Generating perl script..."
- if ! perf script -i "${perfdata}" -g "${generated_script}"; then
- echo "perf script perl test [Failed: script generation for $event_name]"
- return 1
- fi
-
- if [ ! -f "${generated_script}" ]; then
- echo "perf script perl test [Failed: script not generated for $event_name]"
- return 1
- fi
-
- echo "Executing perl script..."
- output=$(perf script -i "${perfdata}" -s "${generated_script}" 2>&1)
-
- if echo "$output" | grep -q "$expected_output"; then
- echo "perf script perl test [Success: $event_name triggered $expected_output]"
- return 0
- else
- echo "perf script perl test [Failed: $event_name did not trigger $expected_output]"
- echo "Output was:"
- echo "$output" | head -n 20
- return 1
- fi
-}
-
-check_perl_support || exit 2
-
-# Try tracepoint first
-test_script "sched:sched_switch" "sched::sched_switch" "-c 1" && res=0 || res=$?
-
-if [ $res -eq 0 ]; then
- exit 0
-elif [ $res -eq 1 ]; then
- exit 1
-fi
-
-# If tracepoint skipped (res=2), try task-clock
-# For generic events like task-clock, the generated script uses process_event()
-# which dumps data using Data::Dumper. We check for "$VAR1" which is standard Dumper output.
-test_script "task-clock" "\$VAR1" "-c 100" && res=0 || res=$?
-
-if [ $res -eq 0 ]; then
- exit 0
-elif [ $res -eq 1 ]; then
- exit 1
-fi
-
-# If both skipped
-echo "perf script perl test [Skipped: Could not record tracepoint or task-clock]"
-exit 2
diff --git a/tools/perf/ui/browsers/scripts.c b/tools/perf/ui/browsers/scripts.c
index 1e8c2c2f952d..27a099af2135 100644
--- a/tools/perf/ui/browsers/scripts.c
+++ b/tools/perf/ui/browsers/scripts.c
@@ -200,14 +200,13 @@ static int find_scripts(char **scripts_array, char **scripts_path_array, int num
if (!strcmp(lang_dirent->d_name, ".") || !strcmp(lang_dirent->d_name, ".."))
continue;
-#ifndef HAVE_LIBPERL_SUPPORT
- if (strstr(lang_dirent->d_name, "perl"))
- continue;
-#endif
+
#ifndef HAVE_LIBPYTHON_SUPPORT
if (strstr(lang_dirent->d_name, "python"))
continue;
#endif
+ if (strstr(lang_dirent->d_name, "perl"))
+ continue;
lang_dir_fd = openat(scripts_dir_fd, lang_dirent->d_name, O_DIRECTORY);
if (lang_dir_fd == -1)
diff --git a/tools/perf/util/scripting-engines/Build b/tools/perf/util/scripting-engines/Build
index 24f087b0cd11..ce14ef44b200 100644
--- a/tools/perf/util/scripting-engines/Build
+++ b/tools/perf/util/scripting-engines/Build
@@ -1,9 +1,7 @@
-ifeq ($(CONFIG_LIBTRACEEVENT),y)
- perf-util-$(CONFIG_LIBPERL) += trace-event-perl.o
-endif
+
perf-util-$(CONFIG_LIBPYTHON) += trace-event-python.o
-CFLAGS_trace-event-perl.o += $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-nested-externs -Wno-undef -Wno-switch-default -Wno-bad-function-cast -Wno-declaration-after-statement -Wno-switch-enum -Wno-thread-safety-analysis
+
# -Wno-declaration-after-statement: The python headers have mixed code with declarations (decls after asserts, for instance)
CFLAGS_trace-event-python.o += $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-deprecated-declarations -Wno-switch-enum -Wno-declaration-after-statement
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c
deleted file mode 100644
index e261a57b87d4..000000000000
--- a/tools/perf/util/scripting-engines/trace-event-perl.c
+++ /dev/null
@@ -1,773 +0,0 @@
-/*
- * trace-event-perl. Feed perf script events to an embedded Perl interpreter.
- *
- * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <inttypes.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-#include <linux/bitmap.h>
-#include <linux/time64.h>
-#include <event-parse.h>
-
-#include <stdbool.h>
-/* perl needs the following define, right after including stdbool.h */
-#define HAS_BOOL
-#include <EXTERN.h>
-#include <perl.h>
-
-#include "../callchain.h"
-#include "../dso.h"
-#include "../machine.h"
-#include "../map.h"
-#include "../symbol.h"
-#include "../thread.h"
-#include "../event.h"
-#include "../trace-event.h"
-#include "../evsel.h"
-#include "../debug.h"
-
-void boot_Perf__Trace__Context(pTHX_ CV *cv);
-void boot_DynaLoader(pTHX_ CV *cv);
-typedef PerlInterpreter * INTERP;
-
-void xs_init(pTHX);
-
-void xs_init(pTHX)
-{
- const char *file = __FILE__;
- dXSUB_SYS;
-
- newXS("Perf::Trace::Context::bootstrap", boot_Perf__Trace__Context,
- file);
- newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
-}
-
-INTERP my_perl;
-
-#define TRACE_EVENT_TYPE_MAX \
- ((1 << (sizeof(unsigned short) * 8)) - 1)
-
-extern struct scripting_context *scripting_context;
-
-static char *cur_field_name;
-static int zero_flag_atom;
-
-static void define_symbolic_value(const char *ev_name,
- const char *field_name,
- const char *field_value,
- const char *field_str)
-{
- unsigned long long value;
- dSP;
-
- value = eval_flag(field_value);
-
- ENTER;
- SAVETMPS;
- PUSHMARK(SP);
-
- XPUSHs(sv_2mortal(newSVpv(ev_name, 0)));
- XPUSHs(sv_2mortal(newSVpv(field_name, 0)));
- XPUSHs(sv_2mortal(newSVuv(value)));
- XPUSHs(sv_2mortal(newSVpv(field_str, 0)));
-
- PUTBACK;
- if (get_cv("main::define_symbolic_value", 0))
- call_pv("main::define_symbolic_value", G_SCALAR);
- SPAGAIN;
- PUTBACK;
- FREETMPS;
- LEAVE;
-}
-
-static void define_symbolic_values(struct tep_print_flag_sym *field,
- const char *ev_name,
- const char *field_name)
-{
- define_symbolic_value(ev_name, field_name, field->value, field->str);
- if (field->next)
- define_symbolic_values(field->next, ev_name, field_name);
-}
-
-static void define_symbolic_field(const char *ev_name,
- const char *field_name)
-{
- dSP;
-
- ENTER;
- SAVETMPS;
- PUSHMARK(SP);
-
- XPUSHs(sv_2mortal(newSVpv(ev_name, 0)));
- XPUSHs(sv_2mortal(newSVpv(field_name, 0)));
-
- PUTBACK;
- if (get_cv("main::define_symbolic_field", 0))
- call_pv("main::define_symbolic_field", G_SCALAR);
- SPAGAIN;
- PUTBACK;
- FREETMPS;
- LEAVE;
-}
-
-static void define_flag_value(const char *ev_name,
- const char *field_name,
- const char *field_value,
- const char *field_str)
-{
- unsigned long long value;
- dSP;
-
- value = eval_flag(field_value);
-
- ENTER;
- SAVETMPS;
- PUSHMARK(SP);
-
- XPUSHs(sv_2mortal(newSVpv(ev_name, 0)));
- XPUSHs(sv_2mortal(newSVpv(field_name, 0)));
- XPUSHs(sv_2mortal(newSVuv(value)));
- XPUSHs(sv_2mortal(newSVpv(field_str, 0)));
-
- PUTBACK;
- if (get_cv("main::define_flag_value", 0))
- call_pv("main::define_flag_value", G_SCALAR);
- SPAGAIN;
- PUTBACK;
- FREETMPS;
- LEAVE;
-}
-
-static void define_flag_values(struct tep_print_flag_sym *field,
- const char *ev_name,
- const char *field_name)
-{
- define_flag_value(ev_name, field_name, field->value, field->str);
- if (field->next)
- define_flag_values(field->next, ev_name, field_name);
-}
-
-static void define_flag_field(const char *ev_name,
- const char *field_name,
- const char *delim)
-{
- dSP;
-
- ENTER;
- SAVETMPS;
- PUSHMARK(SP);
-
- XPUSHs(sv_2mortal(newSVpv(ev_name, 0)));
- XPUSHs(sv_2mortal(newSVpv(field_name, 0)));
- XPUSHs(sv_2mortal(newSVpv(delim, 0)));
-
- PUTBACK;
- if (get_cv("main::define_flag_field", 0))
- call_pv("main::define_flag_field", G_SCALAR);
- SPAGAIN;
- PUTBACK;
- FREETMPS;
- LEAVE;
-}
-
-static void define_event_symbols(struct tep_event *event,
- const char *ev_name,
- struct tep_print_arg *args)
-{
- if (args == NULL)
- return;
-
- switch (args->type) {
- case TEP_PRINT_NULL:
- break;
- case TEP_PRINT_ATOM:
- define_flag_value(ev_name, cur_field_name, "0",
- args->atom.atom);
- zero_flag_atom = 0;
- break;
- case TEP_PRINT_FIELD:
- free(cur_field_name);
- cur_field_name = strdup(args->field.name);
- break;
- case TEP_PRINT_FLAGS:
- define_event_symbols(event, ev_name, args->flags.field);
- define_flag_field(ev_name, cur_field_name, args->flags.delim);
- define_flag_values(args->flags.flags, ev_name, cur_field_name);
- break;
- case TEP_PRINT_SYMBOL:
- define_event_symbols(event, ev_name, args->symbol.field);
- define_symbolic_field(ev_name, cur_field_name);
- define_symbolic_values(args->symbol.symbols, ev_name,
- cur_field_name);
- break;
- case TEP_PRINT_HEX:
- case TEP_PRINT_HEX_STR:
- define_event_symbols(event, ev_name, args->hex.field);
- define_event_symbols(event, ev_name, args->hex.size);
- break;
- case TEP_PRINT_INT_ARRAY:
- define_event_symbols(event, ev_name, args->int_array.field);
- define_event_symbols(event, ev_name, args->int_array.count);
- define_event_symbols(event, ev_name, args->int_array.el_size);
- break;
- case TEP_PRINT_BSTRING:
- case TEP_PRINT_DYNAMIC_ARRAY:
- case TEP_PRINT_DYNAMIC_ARRAY_LEN:
- case TEP_PRINT_STRING:
- case TEP_PRINT_BITMASK:
- break;
- case TEP_PRINT_TYPE:
- define_event_symbols(event, ev_name, args->typecast.item);
- break;
- case TEP_PRINT_OP:
- if (strcmp(args->op.op, ":") == 0)
- zero_flag_atom = 1;
- define_event_symbols(event, ev_name, args->op.left);
- define_event_symbols(event, ev_name, args->op.right);
- break;
- case TEP_PRINT_FUNC:
- default:
- pr_err("Unsupported print arg type\n");
- /* we should warn... */
- return;
- }
-
- if (args->next)
- define_event_symbols(event, ev_name, args->next);
-}
-
-static SV *perl_process_callchain(struct perf_sample *sample,
- struct evsel *evsel,
- struct addr_location *al)
-{
- struct callchain_cursor *cursor;
- AV *list;
-
- list = newAV();
- if (!list)
- goto exit;
-
- if (!symbol_conf.use_callchain || !sample->callchain)
- goto exit;
-
- cursor = get_tls_callchain_cursor();
-
- if (thread__resolve_callchain(al->thread, cursor, evsel,
- sample, NULL, NULL, scripting_max_stack) != 0) {
- pr_err("Failed to resolve callchain. Skipping\n");
- goto exit;
- }
- callchain_cursor_commit(cursor);
-
-
- while (1) {
- HV *elem;
- struct callchain_cursor_node *node;
- node = callchain_cursor_current(cursor);
- if (!node)
- break;
-
- elem = newHV();
- if (!elem)
- goto exit;
-
- if (!hv_stores(elem, "ip", newSVuv(node->ip))) {
- hv_undef(elem);
- goto exit;
- }
-
- if (node->ms.sym) {
- HV *sym = newHV();
- if (!sym) {
- hv_undef(elem);
- goto exit;
- }
- if (!hv_stores(sym, "start", newSVuv(node->ms.sym->start)) ||
- !hv_stores(sym, "end", newSVuv(node->ms.sym->end)) ||
- !hv_stores(sym, "binding", newSVuv(node->ms.sym->binding)) ||
- !hv_stores(sym, "name", newSVpvn(node->ms.sym->name,
- node->ms.sym->namelen)) ||
- !hv_stores(elem, "sym", newRV_noinc((SV*)sym))) {
- hv_undef(sym);
- hv_undef(elem);
- goto exit;
- }
- }
-
- if (node->ms.map) {
- struct map *map = node->ms.map;
- struct dso *dso = map ? map__dso(map) : NULL;
- const char *dsoname = "[unknown]";
-
- if (dso) {
- if (symbol_conf.show_kernel_path && dso__long_name(dso))
- dsoname = dso__long_name(dso);
- else
- dsoname = dso__name(dso);
- }
- if (!hv_stores(elem, "dso", newSVpv(dsoname,0))) {
- hv_undef(elem);
- goto exit;
- }
- }
-
- callchain_cursor_advance(cursor);
- av_push(list, newRV_noinc((SV*)elem));
- }
-
-exit:
- return newRV_noinc((SV*)list);
-}
-
-static void perl_process_tracepoint(struct perf_sample *sample,
- struct evsel *evsel,
- struct addr_location *al)
-{
- struct thread *thread = al->thread;
- struct tep_event *event;
- struct tep_format_field *field;
- static char handler[256];
- unsigned long long val;
- unsigned long s, ns;
- int pid;
- int cpu = sample->cpu;
- void *data = sample->raw_data;
- unsigned long long nsecs = sample->time;
- const char *comm = thread__comm_str(thread);
- DECLARE_BITMAP(events_defined, TRACE_EVENT_TYPE_MAX);
-
- bitmap_zero(events_defined, TRACE_EVENT_TYPE_MAX);
- dSP;
-
- if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
- return;
-
- event = evsel__tp_format(evsel);
- if (!event) {
- pr_debug("ug! no event found for type %" PRIu64, (u64)evsel->core.attr.config);
- return;
- }
-
- pid = raw_field_value(event, "common_pid", data);
-
- sprintf(handler, "%s::%s", event->system, event->name);
-
- if (!__test_and_set_bit(event->id, events_defined))
- define_event_symbols(event, handler, event->print_fmt.args);
-
- s = nsecs / NSEC_PER_SEC;
- ns = nsecs - s * NSEC_PER_SEC;
-
- ENTER;
- SAVETMPS;
- PUSHMARK(SP);
-
- XPUSHs(sv_2mortal(newSVpv(handler, 0)));
- XPUSHs(sv_2mortal(newSViv(PTR2IV(scripting_context))));
- XPUSHs(sv_2mortal(newSVuv(cpu)));
- XPUSHs(sv_2mortal(newSVuv(s)));
- XPUSHs(sv_2mortal(newSVuv(ns)));
- XPUSHs(sv_2mortal(newSViv(pid)));
- XPUSHs(sv_2mortal(newSVpv(comm, 0)));
- XPUSHs(sv_2mortal(perl_process_callchain(sample, evsel, al)));
-
- /* common fields other than pid can be accessed via xsub fns */
-
- for (field = event->format.fields; field; field = field->next) {
- if (field->flags & TEP_FIELD_IS_STRING) {
- int offset;
- if (field->flags & TEP_FIELD_IS_DYNAMIC) {
- offset = *(int *)(data + field->offset);
- offset &= 0xffff;
- if (tep_field_is_relative(field->flags))
- offset += field->offset + field->size;
- } else
- offset = field->offset;
- XPUSHs(sv_2mortal(newSVpv((char *)data + offset, 0)));
- } else { /* FIELD_IS_NUMERIC */
- val = read_size(event, data + field->offset,
- field->size);
- if (field->flags & TEP_FIELD_IS_SIGNED) {
- XPUSHs(sv_2mortal(newSViv(val)));
- } else {
- XPUSHs(sv_2mortal(newSVuv(val)));
- }
- }
- }
-
- PUTBACK;
-
- if (get_cv(handler, 0))
- call_pv(handler, G_SCALAR);
- else if (get_cv("main::trace_unhandled", 0)) {
- XPUSHs(sv_2mortal(newSVpv(handler, 0)));
- XPUSHs(sv_2mortal(newSViv(PTR2IV(scripting_context))));
- XPUSHs(sv_2mortal(newSVuv(cpu)));
- XPUSHs(sv_2mortal(newSVuv(nsecs)));
- XPUSHs(sv_2mortal(newSViv(pid)));
- XPUSHs(sv_2mortal(newSVpv(comm, 0)));
- XPUSHs(sv_2mortal(perl_process_callchain(sample, evsel, al)));
- call_pv("main::trace_unhandled", G_SCALAR);
- }
- SPAGAIN;
- PUTBACK;
- FREETMPS;
- LEAVE;
-}
-
-static void perl_process_event_generic(union perf_event *event,
- struct perf_sample *sample,
- struct evsel *evsel)
-{
- dSP;
-
- if (!get_cv("process_event", 0))
- return;
-
- ENTER;
- SAVETMPS;
- PUSHMARK(SP);
- XPUSHs(sv_2mortal(newSVpvn((const char *)event, event->header.size)));
- XPUSHs(sv_2mortal(newSVpvn((const char *)&evsel->core.attr, sizeof(evsel->core.attr))));
- XPUSHs(sv_2mortal(newSVpvn((const char *)sample, sizeof(*sample))));
- XPUSHs(sv_2mortal(newSVpvn((const char *)sample->raw_data, sample->raw_size)));
- PUTBACK;
- call_pv("process_event", G_SCALAR);
- SPAGAIN;
- PUTBACK;
- FREETMPS;
- LEAVE;
-}
-
-static void perl_process_event(union perf_event *event,
- struct perf_sample *sample,
- struct evsel *evsel,
- struct addr_location *al,
- struct addr_location *addr_al)
-{
- scripting_context__update(scripting_context, event, sample, evsel, al, addr_al);
- perl_process_tracepoint(sample, evsel, al);
- perl_process_event_generic(event, sample, evsel);
-}
-
-static void run_start_sub(void)
-{
- dSP; /* access to Perl stack */
- PUSHMARK(SP);
-
- if (get_cv("main::trace_begin", 0))
- call_pv("main::trace_begin", G_DISCARD | G_NOARGS);
-}
-
-/*
- * Start trace script
- */
-static int perl_start_script(const char *script, int argc, const char **argv,
- struct perf_session *session)
-{
- const char **command_line;
- int i, err = 0;
-
- scripting_context->session = session;
-
- command_line = malloc((argc + 2) * sizeof(const char *));
- if (!command_line)
- return -ENOMEM;
-
- command_line[0] = "";
- command_line[1] = script;
- for (i = 2; i < argc + 2; i++)
- command_line[i] = argv[i - 2];
-
- my_perl = perl_alloc();
- perl_construct(my_perl);
-
- if (perl_parse(my_perl, xs_init, argc + 2, (char **)command_line,
- (char **)NULL)) {
- err = -1;
- goto error;
- }
-
- if (perl_run(my_perl)) {
- err = -1;
- goto error;
- }
-
- if (SvTRUE(ERRSV)) {
- err = -1;
- goto error;
- }
-
- run_start_sub();
-
- free(command_line);
- return 0;
-error:
- perl_free(my_perl);
- free(command_line);
-
- return err;
-}
-
-static int perl_flush_script(void)
-{
- return 0;
-}
-
-/*
- * Stop trace script
- */
-static int perl_stop_script(void)
-{
- dSP; /* access to Perl stack */
- PUSHMARK(SP);
-
- if (get_cv("main::trace_end", 0))
- call_pv("main::trace_end", G_DISCARD | G_NOARGS);
-
- perl_destruct(my_perl);
- perl_free(my_perl);
-
- return 0;
-}
-
-static int perl_generate_script(struct tep_handle *pevent, const char *outfile)
-{
- int i, not_first, count, nr_events;
- struct tep_event **all_events;
- struct tep_event *event = NULL;
- struct tep_format_field *f;
- char fname[PATH_MAX];
- FILE *ofp;
-
- sprintf(fname, "%s.pl", outfile);
- ofp = fopen(fname, "w");
- if (ofp == NULL) {
- fprintf(stderr, "couldn't open %s\n", fname);
- return -1;
- }
-
- fprintf(ofp, "# perf script event handlers, "
- "generated by perf script -g perl\n");
-
- fprintf(ofp, "# Licensed under the terms of the GNU GPL"
- " License version 2\n\n");
-
- fprintf(ofp, "# The common_* event handler fields are the most useful "
- "fields common to\n");
-
- fprintf(ofp, "# all events. They don't necessarily correspond to "
- "the 'common_*' fields\n");
-
- fprintf(ofp, "# in the format files. Those fields not available as "
- "handler params can\n");
-
- fprintf(ofp, "# be retrieved using Perl functions of the form "
- "common_*($context).\n");
-
- fprintf(ofp, "# See Context.pm for the list of available "
- "functions.\n\n");
-
- fprintf(ofp, "use lib \"$ENV{'PERF_EXEC_PATH'}/scripts/perl/"
- "Perf-Trace-Util/lib\";\n");
-
- fprintf(ofp, "use lib \"./Perf-Trace-Util/lib\";\n");
- fprintf(ofp, "use Perf::Trace::Core;\n");
- fprintf(ofp, "use Perf::Trace::Context;\n");
- fprintf(ofp, "use Perf::Trace::Util;\n\n");
-
- fprintf(ofp, "sub trace_begin\n{\n\t# optional\n}\n\n");
- fprintf(ofp, "sub trace_end\n{\n\t# optional\n}\n");
-
-
- fprintf(ofp, "\n\
-sub print_backtrace\n\
-{\n\
- my $callchain = shift;\n\
- for my $node (@$callchain)\n\
- {\n\
- if(exists $node->{sym})\n\
- {\n\
- printf( \"\\t[\\%%x] \\%%s\\n\", $node->{ip}, $node->{sym}{name});\n\
- }\n\
- else\n\
- {\n\
- printf( \"\\t[\\%%x]\\n\", $node{ip});\n\
- }\n\
- }\n\
-}\n\n\
-");
-
- nr_events = tep_get_events_count(pevent);
- all_events = tep_list_events(pevent, TEP_EVENT_SORT_ID);
-
- for (i = 0; all_events && i < nr_events; i++) {
- event = all_events[i];
- fprintf(ofp, "sub %s::%s\n{\n", event->system, event->name);
- fprintf(ofp, "\tmy (");
-
- fprintf(ofp, "$event_name, ");
- fprintf(ofp, "$context, ");
- fprintf(ofp, "$common_cpu, ");
- fprintf(ofp, "$common_secs, ");
- fprintf(ofp, "$common_nsecs,\n");
- fprintf(ofp, "\t $common_pid, ");
- fprintf(ofp, "$common_comm, ");
- fprintf(ofp, "$common_callchain,\n\t ");
-
- not_first = 0;
- count = 0;
-
- for (f = event->format.fields; f; f = f->next) {
- if (not_first++)
- fprintf(ofp, ", ");
- if (++count % 5 == 0)
- fprintf(ofp, "\n\t ");
-
- fprintf(ofp, "$%s", f->name);
- }
- fprintf(ofp, ") = @_;\n\n");
-
- fprintf(ofp, "\tprint_header($event_name, $common_cpu, "
- "$common_secs, $common_nsecs,\n\t "
- "$common_pid, $common_comm, $common_callchain);\n\n");
-
- fprintf(ofp, "\tprintf(\"");
-
- not_first = 0;
- count = 0;
-
- for (f = event->format.fields; f; f = f->next) {
- if (not_first++)
- fprintf(ofp, ", ");
- if (count && count % 4 == 0) {
- fprintf(ofp, "\".\n\t \"");
- }
- count++;
-
- fprintf(ofp, "%s=", f->name);
- if (f->flags & TEP_FIELD_IS_STRING ||
- f->flags & TEP_FIELD_IS_FLAG ||
- f->flags & TEP_FIELD_IS_SYMBOLIC)
- fprintf(ofp, "%%s");
- else if (f->flags & TEP_FIELD_IS_SIGNED)
- fprintf(ofp, "%%d");
- else
- fprintf(ofp, "%%u");
- }
-
- fprintf(ofp, "\\n\",\n\t ");
-
- not_first = 0;
- count = 0;
-
- for (f = event->format.fields; f; f = f->next) {
- if (not_first++)
- fprintf(ofp, ", ");
-
- if (++count % 5 == 0)
- fprintf(ofp, "\n\t ");
-
- if (f->flags & TEP_FIELD_IS_FLAG) {
- if ((count - 1) % 5 != 0) {
- fprintf(ofp, "\n\t ");
- count = 4;
- }
- fprintf(ofp, "flag_str(\"");
- fprintf(ofp, "%s::%s\", ", event->system,
- event->name);
- fprintf(ofp, "\"%s\", $%s)", f->name,
- f->name);
- } else if (f->flags & TEP_FIELD_IS_SYMBOLIC) {
- if ((count - 1) % 5 != 0) {
- fprintf(ofp, "\n\t ");
- count = 4;
- }
- fprintf(ofp, "symbol_str(\"");
- fprintf(ofp, "%s::%s\", ", event->system,
- event->name);
- fprintf(ofp, "\"%s\", $%s)", f->name,
- f->name);
- } else
- fprintf(ofp, "$%s", f->name);
- }
-
- fprintf(ofp, ");\n\n");
-
- fprintf(ofp, "\tprint_backtrace($common_callchain);\n");
-
- fprintf(ofp, "}\n\n");
- }
-
- fprintf(ofp, "sub trace_unhandled\n{\n\tmy ($event_name, $context, "
- "$common_cpu, $common_secs, $common_nsecs,\n\t "
- "$common_pid, $common_comm, $common_callchain) = @_;\n\n");
-
- fprintf(ofp, "\tprint_header($event_name, $common_cpu, "
- "$common_secs, $common_nsecs,\n\t $common_pid, "
- "$common_comm, $common_callchain);\n");
- fprintf(ofp, "\tprint_backtrace($common_callchain);\n");
- fprintf(ofp, "}\n\n");
-
- fprintf(ofp, "sub print_header\n{\n"
- "\tmy ($event_name, $cpu, $secs, $nsecs, $pid, $comm) = @_;\n\n"
- "\tprintf(\"%%-20s %%5u %%05u.%%09u %%8u %%-20s \",\n\t "
- "$event_name, $cpu, $secs, $nsecs, $pid, $comm);\n}\n");
-
- fprintf(ofp,
- "\n# Packed byte string args of process_event():\n"
- "#\n"
- "# $event:\tunion perf_event\tutil/event.h\n"
- "# $attr:\tstruct perf_event_attr\tlinux/perf_event.h\n"
- "# $sample:\tstruct perf_sample\tutil/event.h\n"
- "# $raw_data:\tperf_sample->raw_data\tutil/event.h\n"
- "\n"
- "sub process_event\n"
- "{\n"
- "\tmy ($event, $attr, $sample, $raw_data) = @_;\n"
- "\n"
- "\tmy @event\t= unpack(\"LSS\", $event);\n"
- "\tmy @attr\t= unpack(\"LLQQQQQLLQQ\", $attr);\n"
- "\tmy @sample\t= unpack(\"QLLQQQQQLL\", $sample);\n"
- "\tmy @raw_data\t= unpack(\"C*\", $raw_data);\n"
- "\n"
- "\tuse Data::Dumper;\n"
- "\tprint Dumper \\@event, \\@attr, \\@sample, \\@raw_data;\n"
- "}\n");
-
- fclose(ofp);
-
- fprintf(stderr, "generated Perl script: %s\n", fname);
-
- return 0;
-}
-
-struct scripting_ops perl_scripting_ops = {
- .name = "Perl",
- .dirname = "perl",
- .start_script = perl_start_script,
- .flush_script = perl_flush_script,
- .stop_script = perl_stop_script,
- .process_event = perl_process_event,
- .generate_script = perl_generate_script,
-};
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c
index fa850e44cb46..a82472419611 100644
--- a/tools/perf/util/trace-event-scripting.c
+++ b/tools/perf/util/trace-event-scripting.c
@@ -206,72 +206,7 @@ void setup_python_scripting(void)
}
#endif
-#ifdef HAVE_LIBTRACEEVENT
-static void print_perl_unsupported_msg(void)
-{
- fprintf(stderr, "Perl scripting not supported."
- " Install libperl and rebuild perf to enable it.\n"
- "For example:\n # apt-get install libperl-dev (ubuntu)"
- "\n # yum install 'perl(ExtUtils::Embed)' (Fedora)"
- "\n etc.\n");
-}
-
-static int perl_start_script_unsupported(const char *script __maybe_unused,
- int argc __maybe_unused,
- const char **argv __maybe_unused,
- struct perf_session *session __maybe_unused)
-{
- print_perl_unsupported_msg();
-
- return -1;
-}
-
-static int perl_generate_script_unsupported(struct tep_handle *pevent
- __maybe_unused,
- const char *outfile __maybe_unused)
-{
- print_perl_unsupported_msg();
-
- return -1;
-}
-
-struct scripting_ops perl_scripting_unsupported_ops = {
- .name = "Perl",
- .dirname = "perl",
- .start_script = perl_start_script_unsupported,
- .flush_script = flush_script_unsupported,
- .stop_script = stop_script_unsupported,
- .process_event = process_event_unsupported,
- .generate_script = perl_generate_script_unsupported,
-};
-
-static void register_perl_scripting(struct scripting_ops *scripting_ops)
-{
- if (scripting_context == NULL)
- scripting_context = malloc(sizeof(*scripting_context));
-
- if (scripting_context == NULL ||
- script_spec_register("Perl", scripting_ops) ||
- script_spec_register("pl", scripting_ops)) {
- pr_err("Error registering Perl script extension: disabling it\n");
- zfree(&scripting_context);
- }
-}
-
-#ifndef HAVE_LIBPERL_SUPPORT
-void setup_perl_scripting(void)
-{
- register_perl_scripting(&perl_scripting_unsupported_ops);
-}
-#else
-extern struct scripting_ops perl_scripting_ops;
-void setup_perl_scripting(void)
-{
- register_perl_scripting(&perl_scripting_ops);
-}
-#endif
-#endif
static const struct {
u32 flags;
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index 914d9b69ed62..7bdf44403e3a 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -116,7 +116,7 @@ extern unsigned int scripting_max_stack;
struct scripting_ops *script_spec__lookup(const char *spec);
int script_spec__for_each(int (*cb)(struct scripting_ops *ops, const char *spec));
-void setup_perl_scripting(void);
+
void setup_python_scripting(void);
struct scripting_context {
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v5 56/58] perf script: Refactor to support standalone scripts and remove legacy features
From: Ian Rogers @ 2026-04-24 16:47 UTC (permalink / raw)
To: acme, adrian.hunter, james.clark, leo.yan, namhyung, tmricht
Cc: alice.mei.rogers, dapeng1.mi, linux-arm-kernel, linux-kernel,
linux-perf-users, mingo, peterz, Ian Rogers
In-Reply-To: <20260424164721.2229025-1-irogers@google.com>
- Remove -g / --gen-script option as it is no longer needed.
- Hide -s / --script option to imply running standalone scripts
directly.
- Update find_script to search in 'python' instead of
'scripts/python'.
- Add support for launching standalone scripts using fork and execvp,
skipping the event processing loop.
- Update list_available_scripts to look for .py files directly in
'python' directory.
- Remove all references to scripting_ops and clean up unused functions
and variables.
Assisted-by: Gemini:gemini-3.1-pro-preview
Signed-off-by: Ian Rogers <irogers@google.com>
---
v2:
- Fixed strncat buffer overflow: Updated the strncat call in
find_script() to correctly use the remaining capacity of the
destination buffer instead of sizeof(path) - 1 . The declaration of
len was moved to the top of the function to conform to C style
guidelines.
- Fixed execvp path searching: If find_script() finds a local file in
the current directory without a slash, it now prepends ./ to it.
This ensures that execvp() executed by the child process knows to
look in the current directory rather than searching the system
$PATH .
- Fixed premature loop termination in docstring parsing: Removed a
check in read_script_info() that caused the loop to terminate early
if any fallback description was found (like an SPDX
identifier). This restores the intended behavior of searching the
entire header for a better "description:" tag.
- Updated subcommands and usage: Removed "record" and "report" from
the usage text and stopped passing them as valid subcommands to
parse_options_subcommand() .
- Fixed lost command-line options: Reconstructed the arguments passed
to the standalone script to include -i and the input file path,
ensuring that the user's choice of input file is not lost.
- Added error message on execvp failure: Added a pr_err call in the
child process to print a descriptive error message if execvp()
fails to launch the script.
- Fixed uninitialized status in waitpid : Initialized status to 0 and
verified that waitpid() successfully returned the child's PID
before evaluating its exit status. Also removed unnecessary braces
and fixed indentation in that block.
---
tools/perf/builtin-script.c | 767 +++++++++---------------
tools/perf/util/Build | 1 -
tools/perf/util/scripting-engines/Build | 1 -
tools/perf/util/trace-event-parse.c | 65 --
tools/perf/util/trace-event-scripting.c | 333 ----------
tools/perf/util/trace-event.h | 75 +--
6 files changed, 294 insertions(+), 948 deletions(-)
delete mode 100644 tools/perf/util/scripting-engines/Build
delete mode 100644 tools/perf/util/trace-event-scripting.c
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index c0949556d1bb..9b672edac2ca 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -18,6 +18,7 @@
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/wait.h>
#include <sys/utsname.h>
#include <unistd.h>
@@ -77,7 +78,6 @@
#endif
static char const *script_name;
-static char const *generate_script_lang;
static bool reltime;
static bool deltatime;
static u64 initial_time;
@@ -95,6 +95,7 @@ static int max_blocks;
static struct dlfilter *dlfilter;
static int dlargc;
static char **dlargv;
+static unsigned int scripting_max_stack = PERF_MAX_STACK_DEPTH;
enum perf_output_field {
PERF_OUTPUT_COMM = 1ULL << 0,
@@ -1730,6 +1731,143 @@ static int perf_sample__fprintf_bts(struct perf_sample *sample,
return printed;
}
+#define SAMPLE_FLAGS_BUF_SIZE 64
+#define SAMPLE_FLAGS_STR_ALIGNED_SIZE 21
+
+static int sample_flags_to_name(u32 flags, char *str, size_t size)
+{
+ static const struct {
+ u32 flags;
+ const char *name;
+ } sample_flags[] = {
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL, "call"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN, "return"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL, "jcc"},
+ {PERF_IP_FLAG_BRANCH, "jmp"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_INTERRUPT, "int"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT, "iret"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_SYSCALLRET, "syscall"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_SYSCALLRET, "sysret"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_ASYNC, "async"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_ASYNC | PERF_IP_FLAG_INTERRUPT,
+ "hw int"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TX_ABORT, "tx abrt"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_BEGIN, "tr strt"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_END, "tr end"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_VMENTRY, "vmentry"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_VMEXIT, "vmexit"},
+ {0, NULL}
+ };
+ static const struct {
+ u32 flags;
+ const char *name;
+ } branch_events[] = {
+ {PERF_IP_FLAG_BRANCH_MISS, "miss"},
+ {PERF_IP_FLAG_NOT_TAKEN, "not_taken"},
+ {0, NULL}
+ };
+ int i;
+ const char *prefix;
+ int pos = 0, ret, ev_idx = 0;
+ u32 xf = flags & PERF_ADDITIONAL_STATE_MASK;
+ u32 types, events;
+ char xs[16] = { 0 };
+
+ /* Clear additional state bits */
+ flags &= ~PERF_ADDITIONAL_STATE_MASK;
+
+ if (flags & PERF_IP_FLAG_TRACE_BEGIN)
+ prefix = "tr strt ";
+ else if (flags & PERF_IP_FLAG_TRACE_END)
+ prefix = "tr end ";
+ else
+ prefix = "";
+
+ ret = snprintf(str + pos, size - pos, "%s", prefix);
+ if (ret < 0)
+ return ret;
+ pos += ret;
+
+ flags &= ~(PERF_IP_FLAG_TRACE_BEGIN | PERF_IP_FLAG_TRACE_END);
+
+ types = flags & ~PERF_IP_FLAG_BRANCH_EVENT_MASK;
+ for (i = 0; sample_flags[i].name; i++) {
+ if (sample_flags[i].flags != types)
+ continue;
+
+ ret = snprintf(str + pos, size - pos, "%s", sample_flags[i].name);
+ if (ret < 0)
+ return ret;
+ pos += ret;
+ break;
+ }
+
+ events = flags & PERF_IP_FLAG_BRANCH_EVENT_MASK;
+ for (i = 0; branch_events[i].name; i++) {
+ if (!(branch_events[i].flags & events))
+ continue;
+
+ ret = snprintf(str + pos, size - pos, !ev_idx ? "/%s" : ",%s",
+ branch_events[i].name);
+ if (ret < 0)
+ return ret;
+ pos += ret;
+ ev_idx++;
+ }
+
+ /* Add an end character '/' for events */
+ if (ev_idx) {
+ ret = snprintf(str + pos, size - pos, "/");
+ if (ret < 0)
+ return ret;
+ pos += ret;
+ }
+
+ if (!xf)
+ return pos;
+
+ snprintf(xs, sizeof(xs), "(%s%s%s)",
+ flags & PERF_IP_FLAG_IN_TX ? "x" : "",
+ flags & PERF_IP_FLAG_INTR_DISABLE ? "D" : "",
+ flags & PERF_IP_FLAG_INTR_TOGGLE ? "t" : "");
+
+ /* Right align the string if its length is less than the limit */
+ if ((pos + strlen(xs)) < SAMPLE_FLAGS_STR_ALIGNED_SIZE)
+ ret = snprintf(str + pos, size - pos, "%*s",
+ (int)(SAMPLE_FLAGS_STR_ALIGNED_SIZE - ret), xs);
+ else
+ ret = snprintf(str + pos, size - pos, " %s", xs);
+ if (ret < 0)
+ return ret;
+
+ return pos + ret;
+}
+
+static int perf_sample__sprintf_flags(u32 flags, char *str, size_t sz)
+{
+ const char *chars = PERF_IP_FLAG_CHARS;
+ const size_t n = strlen(PERF_IP_FLAG_CHARS);
+ size_t i, pos = 0;
+ int ret;
+
+ ret = sample_flags_to_name(flags, str, sz);
+ if (ret > 0)
+ return ret;
+
+ for (i = 0; i < n; i++, flags >>= 1) {
+ if ((flags & 1) && pos < sz)
+ str[pos++] = chars[i];
+ }
+ for (; i < 32; i++, flags >>= 1) {
+ if ((flags & 1) && pos < sz)
+ str[pos++] = '?';
+ }
+ if (pos < sz)
+ str[pos] = 0;
+
+ return pos;
+}
+
static int perf_sample__fprintf_flags(u32 flags, FILE *fp)
{
char str[SAMPLE_FLAGS_BUF_SIZE];
@@ -2571,8 +2709,6 @@ static void process_event(struct perf_script *script,
fflush(fp);
}
-static struct scripting_ops *scripting_ops;
-
static void __process_stat(struct evsel *counter, u64 tstamp)
{
int nthreads = perf_thread_map__nr(counter->core.threads);
@@ -2607,35 +2743,14 @@ static void __process_stat(struct evsel *counter, u64 tstamp)
static void process_stat(struct evsel *counter, u64 tstamp)
{
- if (scripting_ops && scripting_ops->process_stat)
- scripting_ops->process_stat(&stat_config, counter, tstamp);
- else
- __process_stat(counter, tstamp);
+ __process_stat(counter, tstamp);
}
-static void process_stat_interval(u64 tstamp)
+static void process_stat_interval(u64 tstamp __maybe_unused)
{
- if (scripting_ops && scripting_ops->process_stat_interval)
- scripting_ops->process_stat_interval(tstamp);
}
-static void setup_scripting(void)
-{
- setup_python_scripting();
-}
-
-static int flush_scripting(void)
-{
- return scripting_ops ? scripting_ops->flush_script() : 0;
-}
-
-static int cleanup_scripting(void)
-{
- pr_debug("\nperf script stopped\n");
-
- return scripting_ops ? scripting_ops->stop_script() : 0;
-}
static bool filter_cpu(struct perf_sample *sample)
{
@@ -2708,19 +2823,7 @@ static int process_sample_event(const struct perf_tool *tool,
goto out_put;
}
- if (scripting_ops) {
- struct addr_location *addr_al_ptr = NULL;
-
- if ((evsel->core.attr.sample_type & PERF_SAMPLE_ADDR) &&
- sample_addr_correlates_sym(&evsel->core.attr)) {
- if (!addr_al.thread)
- thread__resolve(al.thread, &addr_al, sample);
- addr_al_ptr = &addr_al;
- }
- scripting_ops->process_event(event, sample, evsel, &al, addr_al_ptr);
- } else {
- process_event(scr, sample, evsel, &al, &addr_al, machine);
- }
+ process_event(scr, sample, evsel, &al, &addr_al, machine);
out_put:
addr_location__exit(&addr_al);
@@ -3029,8 +3132,7 @@ static int process_switch_event(const struct perf_tool *tool,
if (perf_event__process_switch(tool, event, sample, machine) < 0)
return -1;
- if (scripting_ops && scripting_ops->process_switch && !filter_cpu(sample))
- scripting_ops->process_switch(event, sample, machine);
+
if (!script->show_switch_events)
return 0;
@@ -3039,17 +3141,7 @@ static int process_switch_event(const struct perf_tool *tool,
sample->tid);
}
-static int process_auxtrace_error(const struct perf_tool *tool,
- struct perf_session *session,
- union perf_event *event)
-{
- if (scripting_ops && scripting_ops->process_auxtrace_error) {
- scripting_ops->process_auxtrace_error(session, event);
- return 0;
- }
- return perf_event__process_auxtrace_error(tool, session, event);
-}
static int
process_lost_event(const struct perf_tool *tool,
@@ -3063,12 +3155,11 @@ process_lost_event(const struct perf_tool *tool,
static int
process_throttle_event(const struct perf_tool *tool __maybe_unused,
- union perf_event *event,
- struct perf_sample *sample,
- struct machine *machine)
+ union perf_event *event __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine __maybe_unused)
{
- if (scripting_ops && scripting_ops->process_throttle)
- scripting_ops->process_throttle(event, sample, machine);
+
return 0;
}
@@ -3211,10 +3302,9 @@ static int __cmd_script(struct perf_script *script)
script->tool.mmap = process_mmap_event;
script->tool.mmap2 = process_mmap2_event;
}
- if (script->show_switch_events || (scripting_ops && scripting_ops->process_switch))
+ if (script->show_switch_events)
script->tool.context_switch = process_switch_event;
- if (scripting_ops && scripting_ops->process_auxtrace_error)
- script->tool.auxtrace_error = process_auxtrace_error;
+ script->tool.auxtrace_error = perf_event__process_auxtrace_error;
if (script->show_namespace_events)
script->tool.namespaces = process_namespaces_event;
if (script->show_cgroup_events)
@@ -3251,96 +3341,55 @@ static int __cmd_script(struct perf_script *script)
return ret;
}
-static int list_available_languages_cb(struct scripting_ops *ops, const char *spec)
-{
- fprintf(stderr, " %-42s [%s]\n", spec, ops->name);
- return 0;
-}
-static void list_available_languages(void)
-{
- fprintf(stderr, "\n");
- fprintf(stderr, "Scripting language extensions (used in "
- "perf script -s [spec:]script.[spec]):\n\n");
- script_spec__for_each(&list_available_languages_cb);
- fprintf(stderr, "\n");
-}
/* Find script file relative to current directory or exec path */
static char *find_script(const char *script)
{
char path[PATH_MAX];
+ char *exec_path;
+ size_t len;
- if (!scripting_ops) {
- const char *ext = strrchr(script, '.');
+ if (access(script, R_OK) == 0) {
+ if (!strchr(script, '/')) {
+ snprintf(path, sizeof(path), "./%s", script);
+ script = path;
+ }
+ goto found;
+ }
- if (!ext)
- return NULL;
+ exec_path = get_argv_exec_path();
+ if (!exec_path)
+ return NULL;
- scripting_ops = script_spec__lookup(++ext);
- if (!scripting_ops)
- return NULL;
- }
+ snprintf(path, sizeof(path), "%s/python/%s", exec_path, script);
+ free(exec_path);
+ script = path;
- if (access(script, R_OK)) {
- char *exec_path = get_argv_exec_path();
+ if (access(path, R_OK) == 0)
+ goto found;
- if (!exec_path)
- return NULL;
- snprintf(path, sizeof(path), "%s/scripts/%s/%s",
- exec_path, scripting_ops->dirname, script);
- free(exec_path);
- script = path;
- if (access(script, R_OK))
- return NULL;
- }
+ /* Try with .py suffix. */
+ len = strlen(path);
+
+ strncat(path, ".py", sizeof(path) - len - 1);
+
+ if (access(script, R_OK) == 0)
+ goto found;
+
+ /* Failure to find script. */
+ return NULL;
+
+found:
return strdup(script);
}
static int parse_scriptname(const struct option *opt __maybe_unused,
const char *str, int unset __maybe_unused)
{
- char spec[PATH_MAX];
- const char *script, *ext;
- int len;
-
- if (strcmp(str, "lang") == 0) {
- list_available_languages();
- exit(0);
- }
-
- script = strchr(str, ':');
- if (script) {
- len = script - str;
- if (len >= PATH_MAX) {
- fprintf(stderr, "invalid language specifier");
- return -1;
- }
- strncpy(spec, str, len);
- spec[len] = '\0';
- scripting_ops = script_spec__lookup(spec);
- if (!scripting_ops) {
- fprintf(stderr, "invalid language specifier");
- return -1;
- }
- script++;
- } else {
- script = str;
- ext = strrchr(script, '.');
- if (!ext) {
- fprintf(stderr, "invalid script extension");
- return -1;
- }
- scripting_ops = script_spec__lookup(++ext);
- if (!scripting_ops) {
- fprintf(stderr, "invalid script extension");
- return -1;
- }
- }
-
- script_name = find_script(script);
+ script_name = find_script(str);
if (!script_name)
- script_name = strdup(script);
+ script_name = strdup(str);
return 0;
}
@@ -3551,16 +3600,18 @@ static struct script_desc *script_desc__new(const char *name)
return s;
}
-static void script_desc__delete(struct script_desc *s)
-{
- zfree(&s->name);
- zfree(&s->half_liner);
- zfree(&s->args);
- free(s);
-}
+
static void script_desc__add(struct script_desc *s)
{
+ struct script_desc *pos;
+
+ list_for_each_entry(pos, &script_descs, node) {
+ if (strcasecmp(s->name, pos->name) < 0) {
+ list_add_tail(&s->node, &pos->node);
+ return;
+ }
+ }
list_add_tail(&s->node, &script_descs);
}
@@ -3608,15 +3659,57 @@ static int read_script_info(struct script_desc *desc, const char *filename)
{
char line[BUFSIZ], *p;
FILE *fp;
+ bool in_docstring = false;
+ bool found_description = false;
fp = fopen(filename, "r");
if (!fp)
return -1;
while (fgets(line, sizeof(line), fp)) {
+ static const char * const triple_quote_str[] = {
+ "\"\"\"",
+ "'''",
+ "r\"\"\"",
+ };
p = skip_spaces(line);
if (strlen(p) == 0)
continue;
+
+ if (in_docstring) {
+ if (strlen(p) && p[strlen(p) - 1] == '\n')
+ p[strlen(p) - 1] = '\0';
+ desc->half_liner = strdup(skip_spaces(p));
+ in_docstring = false;
+ found_description = true;
+ break;
+ }
+
+
+ for (size_t i = 0; i < ARRAY_SIZE(triple_quote_str); i++) {
+ const char *quote = triple_quote_str[i];
+
+ if (!strstarts(p, quote))
+ continue;
+
+ p += strlen(quote);
+ p = skip_spaces(p);
+ if (strlen(p) > 0) {
+ if (p[strlen(p) - 1] == '\n')
+ p[strlen(p) - 1] = '\0';
+ p = skip_spaces(p);
+ if (str_ends_with(p, quote))
+ p[strlen(p) - strlen(quote)] = '\0';
+ desc->half_liner = strdup(skip_spaces(p));
+ found_description = true;
+ break;
+ }
+ in_docstring = true;
+ break;
+ }
+ if (in_docstring)
+ continue;
+
if (*p != '#')
continue;
p++;
@@ -3630,13 +3723,15 @@ static int read_script_info(struct script_desc *desc, const char *filename)
if (!strncmp(p, "description:", strlen("description:"))) {
p += strlen("description:");
desc->half_liner = strdup(skip_spaces(p));
- continue;
+ found_description = true;
+ break;
}
- if (!strncmp(p, "args:", strlen("args:"))) {
- p += strlen("args:");
- desc->args = strdup(skip_spaces(p));
- continue;
+ if (!found_description && strlen(p) > 0 &&
+ strncmp(p, "SPDX-License-Identifier", 23)) {
+ desc->half_liner = strdup(p);
+ found_description = true;
+ // Don't break, maybe we find a better "description:" later!
}
}
@@ -3667,9 +3762,9 @@ static int list_available_scripts(const struct option *opt __maybe_unused,
const char *s __maybe_unused,
int unset __maybe_unused)
{
- struct dirent *script_dirent, *lang_dirent;
- char *buf, *scripts_path, *script_path, *lang_path, *first_half;
- DIR *scripts_dir, *lang_dir;
+ struct dirent *script_dirent;
+ char *buf, *scripts_path, *script_path, *first_half;
+ DIR *scripts_dir;
struct script_desc *desc;
char *script_root;
@@ -3680,10 +3775,9 @@ static int list_available_scripts(const struct option *opt __maybe_unused,
}
scripts_path = buf;
script_path = buf + MAXPATHLEN;
- lang_path = buf + 2 * MAXPATHLEN;
first_half = buf + 3 * MAXPATHLEN;
- snprintf(scripts_path, MAXPATHLEN, "%s/scripts", get_argv_exec_path());
+ snprintf(scripts_path, MAXPATHLEN, "%s/python", get_argv_exec_path());
scripts_dir = opendir(scripts_path);
if (!scripts_dir) {
@@ -3695,26 +3789,24 @@ static int list_available_scripts(const struct option *opt __maybe_unused,
exit(-1);
}
- for_each_lang(scripts_path, scripts_dir, lang_dirent) {
- scnprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
- lang_dirent->d_name);
- lang_dir = opendir(lang_path);
- if (!lang_dir)
- continue;
+ while ((script_dirent = readdir(scripts_dir)) != NULL) {
+ if (script_dirent->d_type != DT_DIR &&
+ (script_dirent->d_type != DT_UNKNOWN ||
+ !is_directory(scripts_path, script_dirent))) {
- for_each_script(lang_path, lang_dir, script_dirent) {
- script_root = get_script_root(script_dirent, REPORT_SUFFIX);
+ script_root = get_script_root(script_dirent, ".py");
if (script_root) {
desc = script_desc__findnew(script_root);
scnprintf(script_path, MAXPATHLEN, "%s/%s",
- lang_path, script_dirent->d_name);
+ scripts_path, script_dirent->d_name);
read_script_info(desc, script_path);
free(script_root);
}
}
}
+ closedir(scripts_dir);
- fprintf(stdout, "List of available trace scripts:\n");
+ fprintf(stdout, "List of available scripts:\n");
list_for_each_entry(desc, &script_descs, node) {
sprintf(first_half, "%s %s", desc->name,
desc->args ? desc->args : "");
@@ -3754,93 +3846,7 @@ static void free_dlarg(void)
free(dlargv);
}
-static char *get_script_path(const char *script_root, const char *suffix)
-{
- struct dirent *script_dirent, *lang_dirent;
- char scripts_path[MAXPATHLEN];
- char script_path[MAXPATHLEN];
- DIR *scripts_dir, *lang_dir;
- char lang_path[MAXPATHLEN];
- char *__script_root;
-
- snprintf(scripts_path, MAXPATHLEN, "%s/scripts", get_argv_exec_path());
-
- scripts_dir = opendir(scripts_path);
- if (!scripts_dir)
- return NULL;
-
- for_each_lang(scripts_path, scripts_dir, lang_dirent) {
- scnprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
- lang_dirent->d_name);
- lang_dir = opendir(lang_path);
- if (!lang_dir)
- continue;
-
- for_each_script(lang_path, lang_dir, script_dirent) {
- __script_root = get_script_root(script_dirent, suffix);
- if (__script_root && !strcmp(script_root, __script_root)) {
- free(__script_root);
- closedir(scripts_dir);
- scnprintf(script_path, MAXPATHLEN, "%s/%s",
- lang_path, script_dirent->d_name);
- closedir(lang_dir);
- return strdup(script_path);
- }
- free(__script_root);
- }
- closedir(lang_dir);
- }
- closedir(scripts_dir);
-
- return NULL;
-}
-
-static bool is_top_script(const char *script_path)
-{
- return ends_with(script_path, "top") != NULL;
-}
-
-static int has_required_arg(char *script_path)
-{
- struct script_desc *desc;
- int n_args = 0;
- char *p;
-
- desc = script_desc__new(NULL);
-
- if (read_script_info(desc, script_path))
- goto out;
-
- if (!desc->args)
- goto out;
-
- for (p = desc->args; *p; p++)
- if (*p == '<')
- n_args++;
-out:
- script_desc__delete(desc);
-
- return n_args;
-}
-
-static int have_cmd(int argc, const char **argv)
-{
- char **__argv = calloc(argc, sizeof(const char *));
- if (!__argv) {
- pr_err("malloc failed\n");
- return -1;
- }
-
- memcpy(__argv, argv, sizeof(const char *) * argc);
- argc = parse_options(argc, (const char **)__argv, record_options,
- NULL, PARSE_OPT_STOP_AT_NON_OPTION);
- free(__argv);
-
- system_wide = (argc == 0);
-
- return 0;
-}
static void script__setup_sample_type(struct perf_script *script)
{
@@ -4026,17 +4032,13 @@ int cmd_script(int argc, const char **argv)
bool show_full_info = false;
bool header = false;
bool header_only = false;
- bool script_started = false;
bool unsorted_dump = false;
bool merge_deferred_callchains = true;
- char *rec_script_path = NULL;
- char *rep_script_path = NULL;
struct perf_session *session;
struct itrace_synth_opts itrace_synth_opts = {
.set = false,
.default_no_sample = true,
};
- char *script_path = NULL;
const char *dlfilter_file = NULL;
const char **__argv;
int i, j, err = 0;
@@ -4057,11 +4059,10 @@ int cmd_script(int argc, const char **argv)
list_available_scripts),
OPT_CALLBACK_NOOPT(0, "list-dlfilters", NULL, NULL, "list available dlfilters",
list_available_dlfilters),
- OPT_CALLBACK('s', "script", NULL, "name",
- "script file name (lang:script name, script name, or *)",
- parse_scriptname),
- OPT_STRING('g', "gen-script", &generate_script_lang, "lang",
- "generate perf-script.xx script in specified language"),
+ { .type = OPTION_CALLBACK, .short_name = 's', .long_name = "script",
+ .value = NULL, .argh = "name",
+ .help = "script file name (lang:script name, script name, or *)",
+ .callback = parse_scriptname, .flags = PARSE_OPT_HIDDEN },
OPT_STRING(0, "dlfilter", &dlfilter_file, "file", "filter .so file name"),
OPT_CALLBACK(0, "dlarg", NULL, "argument", "filter argument",
add_dlarg),
@@ -4185,22 +4186,17 @@ int cmd_script(int argc, const char **argv)
OPTS_EVSWITCH(&script.evswitch),
OPT_END()
};
- const char * const script_subcommands[] = { "record", "report", NULL };
const char *script_usage[] = {
"perf script [<options>]",
- "perf script [<options>] record <script> [<record-options>] <command>",
- "perf script [<options>] report <script> [script-args]",
- "perf script [<options>] <script> [<record-options>] <command>",
- "perf script [<options>] <top-script> [script-args]",
+ "perf script [<options>] <script> [script-args]",
NULL
};
struct perf_env *env;
perf_set_singlethreaded();
- setup_scripting();
- argc = parse_options_subcommand(argc, argv, options, script_subcommands, script_usage,
+ argc = parse_options_subcommand(argc, argv, options, NULL, script_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (symbol_conf.guestmount ||
@@ -4223,21 +4219,7 @@ int cmd_script(int argc, const char **argv)
if (symbol__validate_sym_arguments())
return -1;
- if (argc > 1 && strlen(argv[0]) > 2 && strstarts("record", argv[0])) {
- rec_script_path = get_script_path(argv[1], RECORD_SUFFIX);
- if (!rec_script_path)
- return cmd_record(argc, argv);
- }
- if (argc > 1 && strlen(argv[0]) > 2 && strstarts("report", argv[0])) {
- rep_script_path = get_script_path(argv[1], REPORT_SUFFIX);
- if (!rep_script_path) {
- fprintf(stderr,
- "Please specify a valid report script"
- "(see 'perf script -l' for listing)\n");
- return -1;
- }
- }
if (reltime && deltatime) {
fprintf(stderr,
@@ -4253,149 +4235,54 @@ int cmd_script(int argc, const char **argv)
/* make sure PERF_EXEC_PATH is set for scripts */
set_argv_exec_path(get_argv_exec_path());
- if (argc && !script_name && !rec_script_path && !rep_script_path) {
- int live_pipe[2];
- int rep_args;
- pid_t pid;
-
- rec_script_path = get_script_path(argv[0], RECORD_SUFFIX);
- rep_script_path = get_script_path(argv[0], REPORT_SUFFIX);
-
- if (!rec_script_path && !rep_script_path) {
- script_name = find_script(argv[0]);
- if (script_name) {
- argc -= 1;
- argv += 1;
- goto script_found;
- }
- usage_with_options_msg(script_usage, options,
- "Couldn't find script `%s'\n\n See perf"
- " script -l for available scripts.\n", argv[0]);
- }
-
- if (is_top_script(argv[0])) {
- rep_args = argc - 1;
- } else {
- int rec_args;
-
- rep_args = has_required_arg(rep_script_path);
- rec_args = (argc - 1) - rep_args;
- if (rec_args < 0) {
- usage_with_options_msg(script_usage, options,
- "`%s' script requires options."
- "\n\n See perf script -l for available "
- "scripts and options.\n", argv[0]);
- }
+ if (argc && !script_name) {
+ script_name = find_script(argv[0]);
+ if (script_name) {
+ argc -= 1;
+ argv += 1;
+ goto script_found;
}
+ usage_with_options_msg(script_usage, options,
+ "Couldn't find script `%s'\n\n"
+ " See perf script -l for available scripts.\n", argv[0]);
+ }
+script_found:
- if (pipe(live_pipe) < 0) {
- perror("failed to create pipe");
- return -1;
- }
- pid = fork();
+ if (script_name) {
+ pid_t pid = fork();
if (pid < 0) {
- perror("failed to fork");
- return -1;
+ pr_err("failed to fork\n");
+ return -errno;
}
-
- if (!pid) {
+ if (pid == 0) { /* child */
+ __argv = calloc(argc + 4, sizeof(const char *));
j = 0;
-
- dup2(live_pipe[1], 1);
- close(live_pipe[0]);
-
- if (is_top_script(argv[0])) {
- system_wide = true;
- } else if (!system_wide) {
- if (have_cmd(argc - rep_args, &argv[rep_args]) != 0) {
- err = -1;
- goto out;
- }
- }
-
- __argv = calloc(argc + 6, sizeof(const char *));
if (!__argv) {
- pr_err("malloc failed\n");
- err = -ENOMEM;
- goto out;
+ exit(-ENOMEM);
}
-
- __argv[j++] = "/bin/sh";
- __argv[j++] = rec_script_path;
- if (system_wide)
- __argv[j++] = "-a";
- __argv[j++] = "-q";
- __argv[j++] = "-o";
- __argv[j++] = "-";
- for (i = rep_args + 1; i < argc; i++)
+ __argv[j++] = script_name;
+ if (input_name) {
+ __argv[j++] = "-i";
+ __argv[j++] = input_name;
+ }
+ for (i = 0; i < argc; i++)
__argv[j++] = argv[i];
__argv[j++] = NULL;
- execvp("/bin/sh", (char **)__argv);
- free(__argv);
- exit(-1);
- }
-
- dup2(live_pipe[0], 0);
- close(live_pipe[1]);
-
- __argv = calloc(argc + 4, sizeof(const char *));
- if (!__argv) {
- pr_err("malloc failed\n");
- err = -ENOMEM;
- goto out;
- }
-
- j = 0;
- __argv[j++] = "/bin/sh";
- __argv[j++] = rep_script_path;
- for (i = 1; i < rep_args + 1; i++)
- __argv[j++] = argv[i];
- __argv[j++] = "-i";
- __argv[j++] = "-";
- __argv[j++] = NULL;
-
- execvp("/bin/sh", (char **)__argv);
- free(__argv);
- exit(-1);
- }
-script_found:
- if (rec_script_path)
- script_path = rec_script_path;
- if (rep_script_path)
- script_path = rep_script_path;
-
- if (script_path) {
- j = 0;
-
- if (!rec_script_path)
- system_wide = false;
- else if (!system_wide) {
- if (have_cmd(argc - 1, &argv[1]) != 0) {
- err = -1;
- goto out;
+ execvp(script_name, (char **)__argv);
+ pr_err("failed to execute script '%s': %s\n", script_name, strerror(errno));
+ exit(-errno);
+ } else { /* parent */
+ int status = 0;
+
+ if (waitpid(pid, &status, 0) == pid) {
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ else
+ return -1;
}
}
-
- __argv = calloc(argc + 2, sizeof(const char *));
- if (!__argv) {
- pr_err("malloc failed\n");
- err = -ENOMEM;
- goto out;
- }
-
- __argv[j++] = "/bin/sh";
- __argv[j++] = script_path;
- if (system_wide)
- __argv[j++] = "-a";
- for (i = 2; i < argc; i++)
- __argv[j++] = argv[i];
- __argv[j++] = NULL;
-
- execvp("/bin/sh", (char **)__argv);
- free(__argv);
- exit(-1);
}
if (dlfilter_file) {
@@ -4487,77 +4374,12 @@ int cmd_script(int argc, const char **argv)
goto out_delete;
}
#endif
- if (generate_script_lang) {
- struct stat perf_stat;
- int input;
- char *filename = strdup("perf-script");
-
- if (output_set_by_user()) {
- fprintf(stderr,
- "custom fields not supported for generated scripts");
- err = -EINVAL;
- goto out_delete;
- }
-
- input = open(data.path, O_RDONLY); /* input_name */
- if (input < 0) {
- err = -errno;
- perror("failed to open file");
- goto out_delete;
- }
-
- err = fstat(input, &perf_stat);
- if (err < 0) {
- perror("failed to stat file");
- goto out_delete;
- }
-
- if (!perf_stat.st_size) {
- fprintf(stderr, "zero-sized file, nothing to do!\n");
- goto out_delete;
- }
-
- scripting_ops = script_spec__lookup(generate_script_lang);
- if (!scripting_ops && ends_with(generate_script_lang, ".py")) {
- scripting_ops = script_spec__lookup("python");
- free(filename);
- filename = strdup(generate_script_lang);
- filename[strlen(filename) - 3] = '\0';
- } else if (!scripting_ops && ends_with(generate_script_lang, ".pl")) {
- scripting_ops = script_spec__lookup("perl");
- free(filename);
- filename = strdup(generate_script_lang);
- filename[strlen(filename) - 3] = '\0';
- }
- if (!scripting_ops) {
- fprintf(stderr, "invalid language specifier '%s'\n", generate_script_lang);
- err = -ENOENT;
- goto out_delete;
- }
- if (!filename) {
- err = -ENOMEM;
- goto out_delete;
- }
-#ifdef HAVE_LIBTRACEEVENT
- err = scripting_ops->generate_script(session->tevent.pevent, filename);
-#else
- err = scripting_ops->generate_script(NULL, filename);
-#endif
- free(filename);
- goto out_delete;
- }
err = dlfilter__start(dlfilter, session);
if (err)
goto out_delete;
- if (script_name) {
- err = scripting_ops->start_script(script_name, argc, argv, session);
- if (err)
- goto out_delete;
- pr_debug("perf script started with script %s\n\n", script_name);
- script_started = true;
- }
+
err = perf_session__check_output_opt(session);
@@ -4587,7 +4409,6 @@ int cmd_script(int argc, const char **argv)
err = __cmd_script(&script);
- flush_scripting();
if (verbose > 2 || debug_kmaps)
perf_session__dump_kmaps(session);
@@ -4603,10 +4424,8 @@ int cmd_script(int argc, const char **argv)
perf_session__delete(session);
perf_script__exit(&script);
- if (script_started)
- cleanup_scripting();
+
dlfilter__cleanup(dlfilter);
free_dlarg();
-out:
return err;
}
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 70cc91d00804..91457de2ea18 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -94,7 +94,6 @@ perf-util-y += tool_pmu.o
perf-util-y += tp_pmu.o
perf-util-y += svghelper.o
perf-util-y += trace-event-info.o
-perf-util-y += trace-event-scripting.o
perf-util-$(CONFIG_LIBTRACEEVENT) += trace-event.o
perf-util-$(CONFIG_LIBTRACEEVENT) += trace-event-parse.o
perf-util-$(CONFIG_LIBTRACEEVENT) += trace-event-read.o
diff --git a/tools/perf/util/scripting-engines/Build b/tools/perf/util/scripting-engines/Build
deleted file mode 100644
index 54920e7e1d5d..000000000000
--- a/tools/perf/util/scripting-engines/Build
+++ /dev/null
@@ -1 +0,0 @@
-# No embedded scripting engines
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c
index 9c015fc2bcfb..374cf82fd86e 100644
--- a/tools/perf/util/trace-event-parse.c
+++ b/tools/perf/util/trace-event-parse.c
@@ -14,71 +14,6 @@
#include <linux/kernel.h>
#include <event-parse.h>
-static int get_common_field(struct scripting_context *context,
- int *offset, int *size, const char *type)
-{
- struct tep_handle *pevent = context->pevent;
- struct tep_event *event;
- struct tep_format_field *field;
-
- if (!*size) {
-
- event = tep_get_first_event(pevent);
- if (!event)
- return 0;
-
- field = tep_find_common_field(event, type);
- if (!field)
- return 0;
- *offset = field->offset;
- *size = field->size;
- }
-
- return tep_read_number(pevent, context->event_data + *offset, *size);
-}
-
-int common_lock_depth(struct scripting_context *context)
-{
- static int offset;
- static int size;
- int ret;
-
- ret = get_common_field(context, &size, &offset,
- "common_lock_depth");
- if (ret < 0)
- return -1;
-
- return ret;
-}
-
-int common_flags(struct scripting_context *context)
-{
- static int offset;
- static int size;
- int ret;
-
- ret = get_common_field(context, &size, &offset,
- "common_flags");
- if (ret < 0)
- return -1;
-
- return ret;
-}
-
-int common_pc(struct scripting_context *context)
-{
- static int offset;
- static int size;
- int ret;
-
- ret = get_common_field(context, &size, &offset,
- "common_preempt_count");
- if (ret < 0)
- return -1;
-
- return ret;
-}
-
unsigned long long
raw_field_value(struct tep_event *event, const char *name, void *data)
{
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c
deleted file mode 100644
index 0a0a50d9e1e1..000000000000
--- a/tools/perf/util/trace-event-scripting.c
+++ /dev/null
@@ -1,333 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * trace-event-scripting. Scripting engine common and initialization code.
- *
- * Copyright (C) 2009-2010 Tom Zanussi <tzanussi@gmail.com>
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#ifdef HAVE_LIBTRACEEVENT
-#include <event-parse.h>
-#endif
-
-#include "debug.h"
-#include "event.h"
-#include "trace-event.h"
-#include "evsel.h"
-#include <linux/perf_event.h>
-#include <linux/zalloc.h>
-#include "util/sample.h"
-
-unsigned int scripting_max_stack = PERF_MAX_STACK_DEPTH;
-
-struct scripting_context *scripting_context;
-
-struct script_spec {
- struct list_head node;
- struct scripting_ops *ops;
- char spec[];
-};
-
-static LIST_HEAD(script_specs);
-
-static struct script_spec *script_spec__new(const char *spec,
- struct scripting_ops *ops)
-{
- struct script_spec *s = malloc(sizeof(*s) + strlen(spec) + 1);
-
- if (s != NULL) {
- strcpy(s->spec, spec);
- s->ops = ops;
- }
-
- return s;
-}
-
-static void script_spec__add(struct script_spec *s)
-{
- list_add_tail(&s->node, &script_specs);
-}
-
-static struct script_spec *script_spec__find(const char *spec)
-{
- struct script_spec *s;
-
- list_for_each_entry(s, &script_specs, node)
- if (strcasecmp(s->spec, spec) == 0)
- return s;
- return NULL;
-}
-
-static int script_spec_register(const char *spec, struct scripting_ops *ops)
-{
- struct script_spec *s;
-
- s = script_spec__find(spec);
- if (s)
- return -1;
-
- s = script_spec__new(spec, ops);
- if (!s)
- return -1;
-
- script_spec__add(s);
- return 0;
-}
-
-struct scripting_ops *script_spec__lookup(const char *spec)
-{
- struct script_spec *s = script_spec__find(spec);
-
- if (!s)
- return NULL;
-
- return s->ops;
-}
-
-int script_spec__for_each(int (*cb)(struct scripting_ops *ops, const char *spec))
-{
- struct script_spec *s;
- int ret = 0;
-
- list_for_each_entry(s, &script_specs, node) {
- ret = cb(s->ops, s->spec);
- if (ret)
- break;
- }
- return ret;
-}
-
-void scripting_context__update(struct scripting_context *c,
- union perf_event *event,
- struct perf_sample *sample,
- struct evsel *evsel,
- struct addr_location *al,
- struct addr_location *addr_al)
-{
-#ifdef HAVE_LIBTRACEEVENT
- const struct tep_event *tp_format = evsel__tp_format(evsel);
-
- c->pevent = tp_format ? tp_format->tep : NULL;
-#else
- c->pevent = NULL;
-#endif
- c->event_data = sample->raw_data;
- c->event = event;
- c->sample = sample;
- c->evsel = evsel;
- c->al = al;
- c->addr_al = addr_al;
-}
-
-static int flush_script_unsupported(void)
-{
- return 0;
-}
-
-static int stop_script_unsupported(void)
-{
- return 0;
-}
-
-static void process_event_unsupported(union perf_event *event __maybe_unused,
- struct perf_sample *sample __maybe_unused,
- struct evsel *evsel __maybe_unused,
- struct addr_location *al __maybe_unused,
- struct addr_location *addr_al __maybe_unused)
-{
-} static void print_python_unsupported_msg(void)
-{
- fprintf(stderr, "Python scripting not supported."
- " Install libpython and rebuild perf to enable it.\n"
- "For example:\n # apt-get install python-dev (ubuntu)"
- "\n # yum install python-devel (Fedora)"
- "\n etc.\n");
-}
-
-static int python_start_script_unsupported(const char *script __maybe_unused,
- int argc __maybe_unused,
- const char **argv __maybe_unused,
- struct perf_session *session __maybe_unused)
-{
- print_python_unsupported_msg();
-
- return -1;
-}
-
-static int python_generate_script_unsupported(struct tep_handle *pevent
- __maybe_unused,
- const char *outfile
- __maybe_unused)
-{
- print_python_unsupported_msg();
-
- return -1;
-}
-
-struct scripting_ops python_scripting_unsupported_ops = {
- .name = "Python",
- .dirname = "python",
- .start_script = python_start_script_unsupported,
- .flush_script = flush_script_unsupported,
- .stop_script = stop_script_unsupported,
- .process_event = process_event_unsupported,
- .generate_script = python_generate_script_unsupported,
-};
-
-static void register_python_scripting(struct scripting_ops *scripting_ops)
-{
- if (scripting_context == NULL)
- scripting_context = malloc(sizeof(*scripting_context));
-
- if (scripting_context == NULL ||
- script_spec_register("Python", scripting_ops) ||
- script_spec_register("py", scripting_ops)) {
- pr_err("Error registering Python script extension: disabling it\n");
- zfree(&scripting_context);
- }
-}
-
-void setup_python_scripting(void)
-{
- register_python_scripting(&python_scripting_unsupported_ops);
-}
-
-
-static const struct {
- u32 flags;
- const char *name;
-} sample_flags[] = {
- {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL, "call"},
- {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN, "return"},
- {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL, "jcc"},
- {PERF_IP_FLAG_BRANCH, "jmp"},
- {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_INTERRUPT, "int"},
- {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT, "iret"},
- {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_SYSCALLRET, "syscall"},
- {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_SYSCALLRET, "sysret"},
- {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_ASYNC, "async"},
- {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_ASYNC | PERF_IP_FLAG_INTERRUPT,
- "hw int"},
- {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TX_ABORT, "tx abrt"},
- {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_BEGIN, "tr strt"},
- {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_END, "tr end"},
- {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_VMENTRY, "vmentry"},
- {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_VMEXIT, "vmexit"},
- {0, NULL}
-};
-
-static const struct {
- u32 flags;
- const char *name;
-} branch_events[] = {
- {PERF_IP_FLAG_BRANCH_MISS, "miss"},
- {PERF_IP_FLAG_NOT_TAKEN, "not_taken"},
- {0, NULL}
-};
-
-static int sample_flags_to_name(u32 flags, char *str, size_t size)
-{
- int i;
- const char *prefix;
- int pos = 0, ret, ev_idx = 0;
- u32 xf = flags & PERF_ADDITIONAL_STATE_MASK;
- u32 types, events;
- char xs[16] = { 0 };
-
- /* Clear additional state bits */
- flags &= ~PERF_ADDITIONAL_STATE_MASK;
-
- if (flags & PERF_IP_FLAG_TRACE_BEGIN)
- prefix = "tr strt ";
- else if (flags & PERF_IP_FLAG_TRACE_END)
- prefix = "tr end ";
- else
- prefix = "";
-
- ret = snprintf(str + pos, size - pos, "%s", prefix);
- if (ret < 0)
- return ret;
- pos += ret;
-
- flags &= ~(PERF_IP_FLAG_TRACE_BEGIN | PERF_IP_FLAG_TRACE_END);
-
- types = flags & ~PERF_IP_FLAG_BRANCH_EVENT_MASK;
- for (i = 0; sample_flags[i].name; i++) {
- if (sample_flags[i].flags != types)
- continue;
-
- ret = snprintf(str + pos, size - pos, "%s", sample_flags[i].name);
- if (ret < 0)
- return ret;
- pos += ret;
- break;
- }
-
- events = flags & PERF_IP_FLAG_BRANCH_EVENT_MASK;
- for (i = 0; branch_events[i].name; i++) {
- if (!(branch_events[i].flags & events))
- continue;
-
- ret = snprintf(str + pos, size - pos, !ev_idx ? "/%s" : ",%s",
- branch_events[i].name);
- if (ret < 0)
- return ret;
- pos += ret;
- ev_idx++;
- }
-
- /* Add an end character '/' for events */
- if (ev_idx) {
- ret = snprintf(str + pos, size - pos, "/");
- if (ret < 0)
- return ret;
- pos += ret;
- }
-
- if (!xf)
- return pos;
-
- snprintf(xs, sizeof(xs), "(%s%s%s)",
- flags & PERF_IP_FLAG_IN_TX ? "x" : "",
- flags & PERF_IP_FLAG_INTR_DISABLE ? "D" : "",
- flags & PERF_IP_FLAG_INTR_TOGGLE ? "t" : "");
-
- /* Right align the string if its length is less than the limit */
- if ((pos + strlen(xs)) < SAMPLE_FLAGS_STR_ALIGNED_SIZE)
- ret = snprintf(str + pos, size - pos, "%*s",
- (int)(SAMPLE_FLAGS_STR_ALIGNED_SIZE - ret), xs);
- else
- ret = snprintf(str + pos, size - pos, " %s", xs);
- if (ret < 0)
- return ret;
-
- return pos + ret;
-}
-
-int perf_sample__sprintf_flags(u32 flags, char *str, size_t sz)
-{
- const char *chars = PERF_IP_FLAG_CHARS;
- const size_t n = strlen(PERF_IP_FLAG_CHARS);
- size_t i, pos = 0;
- int ret;
-
- ret = sample_flags_to_name(flags, str, sz);
- if (ret > 0)
- return ret;
-
- for (i = 0; i < n; i++, flags >>= 1) {
- if ((flags & 1) && pos < sz)
- str[pos++] = chars[i];
- }
- for (; i < 32; i++, flags >>= 1) {
- if ((flags & 1) && pos < sz)
- str[pos++] = '?';
- }
- if (pos < sz)
- str[pos] = 0;
-
- return pos;
-}
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index 7bdf44403e3a..19f22ac1faf3 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -7,15 +7,9 @@
#include <sys/types.h>
#include <linux/types.h>
-struct evlist;
struct machine;
-struct perf_sample;
-union perf_event;
-struct perf_tool;
-struct thread;
-struct tep_plugin_list;
-struct evsel;
struct tep_format_field;
+struct tep_plugin_list;
struct trace_event {
struct tep_handle *pevent;
@@ -79,73 +73,6 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs,
int fd, bool temp);
int tracing_data_put(struct tracing_data *tdata);
-
-struct addr_location;
-
-struct perf_session;
-struct perf_stat_config;
-
-struct scripting_ops {
- const char *name;
- const char *dirname; /* For script path .../scripts/<dirname>/... */
- int (*start_script)(const char *script, int argc, const char **argv,
- struct perf_session *session);
- int (*flush_script) (void);
- int (*stop_script) (void);
- void (*process_event) (union perf_event *event,
- struct perf_sample *sample,
- struct evsel *evsel,
- struct addr_location *al,
- struct addr_location *addr_al);
- void (*process_switch)(union perf_event *event,
- struct perf_sample *sample,
- struct machine *machine);
- void (*process_auxtrace_error)(struct perf_session *session,
- union perf_event *event);
- void (*process_stat)(struct perf_stat_config *config,
- struct evsel *evsel, u64 tstamp);
- void (*process_stat_interval)(u64 tstamp);
- void (*process_throttle)(union perf_event *event,
- struct perf_sample *sample,
- struct machine *machine);
- int (*generate_script) (struct tep_handle *pevent, const char *outfile);
-};
-
-extern unsigned int scripting_max_stack;
-
-struct scripting_ops *script_spec__lookup(const char *spec);
-int script_spec__for_each(int (*cb)(struct scripting_ops *ops, const char *spec));
-
-
-void setup_python_scripting(void);
-
-struct scripting_context {
- struct tep_handle *pevent;
- void *event_data;
- union perf_event *event;
- struct perf_sample *sample;
- struct evsel *evsel;
- struct addr_location *al;
- struct addr_location *addr_al;
- struct perf_session *session;
-};
-
-void scripting_context__update(struct scripting_context *scripting_context,
- union perf_event *event,
- struct perf_sample *sample,
- struct evsel *evsel,
- struct addr_location *al,
- struct addr_location *addr_al);
-
-int common_pc(struct scripting_context *context);
-int common_flags(struct scripting_context *context);
-int common_lock_depth(struct scripting_context *context);
-
-#define SAMPLE_FLAGS_BUF_SIZE 64
-#define SAMPLE_FLAGS_STR_ALIGNED_SIZE 21
-
-int perf_sample__sprintf_flags(u32 flags, char *str, size_t sz);
-
#if defined(LIBTRACEEVENT_VERSION) && LIBTRACEEVENT_VERSION >= MAKE_LIBTRACEEVENT_VERSION(1, 5, 0)
#include <event-parse.h>
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v5 57/58] perf Documentation: Update for standalone Python scripts and remove obsolete data
From: Ian Rogers @ 2026-04-24 16:47 UTC (permalink / raw)
To: acme, adrian.hunter, james.clark, leo.yan, namhyung, tmricht
Cc: alice.mei.rogers, dapeng1.mi, linux-arm-kernel, linux-kernel,
linux-perf-users, mingo, peterz, Ian Rogers
In-Reply-To: <20260424164721.2229025-1-irogers@google.com>
- Remove documentation for -g and -s options in perf-script.txt.
- Remove links to perf-script-perl in perf-script.txt.
- Rewrite perf-script-python.txt to describe the new standalone script
usage with perf module.
- Remove obsolete perf-script-perl.txt.
Assisted-by: Gemini:gemini-3.1-pro-preview
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/Documentation/perf-script-perl.txt | 216 ------
.../perf/Documentation/perf-script-python.txt | 702 +++---------------
tools/perf/Documentation/perf-script.txt | 70 +-
3 files changed, 95 insertions(+), 893 deletions(-)
delete mode 100644 tools/perf/Documentation/perf-script-perl.txt
diff --git a/tools/perf/Documentation/perf-script-perl.txt b/tools/perf/Documentation/perf-script-perl.txt
deleted file mode 100644
index 5b479f5e62ff..000000000000
--- a/tools/perf/Documentation/perf-script-perl.txt
+++ /dev/null
@@ -1,216 +0,0 @@
-perf-script-perl(1)
-===================
-
-NAME
-----
-perf-script-perl - Process trace data with a Perl script
-
-SYNOPSIS
---------
-[verse]
-'perf script' [-s [Perl]:script[.pl] ]
-
-DESCRIPTION
------------
-
-This perf script option is used to process perf script data using perf's
-built-in Perl interpreter. It reads and processes the input file and
-displays the results of the trace analysis implemented in the given
-Perl script, if any.
-
-STARTER SCRIPTS
----------------
-
-You can avoid reading the rest of this document by running 'perf script
--g perl' in the same directory as an existing perf.data trace file.
-That will generate a starter script containing a handler for each of
-the event types in the trace file; it simply prints every available
-field for each event in the trace file.
-
-You can also look at the existing scripts in
-~/libexec/perf-core/scripts/perl for typical examples showing how to
-do basic things like aggregate event data, print results, etc. Also,
-the check-perf-script.pl script, while not interesting for its results,
-attempts to exercise all of the main scripting features.
-
-EVENT HANDLERS
---------------
-
-When perf script is invoked using a trace script, a user-defined
-'handler function' is called for each event in the trace. If there's
-no handler function defined for a given event type, the event is
-ignored (or passed to a 'trace_unhandled' function, see below) and the
-next event is processed.
-
-Most of the event's field values are passed as arguments to the
-handler function; some of the less common ones aren't - those are
-available as calls back into the perf executable (see below).
-
-As an example, the following perf record command can be used to record
-all sched_wakeup events in the system:
-
- # perf record -a -e sched:sched_wakeup
-
-Traces meant to be processed using a script should be recorded with
-the above option: -a to enable system-wide collection.
-
-The format file for the sched_wakeup event defines the following fields
-(see /sys/kernel/tracing/events/sched/sched_wakeup/format):
-
-----
- format:
- field:unsigned short common_type;
- field:unsigned char common_flags;
- field:unsigned char common_preempt_count;
- field:int common_pid;
-
- field:char comm[TASK_COMM_LEN];
- field:pid_t pid;
- field:int prio;
- field:int success;
- field:int target_cpu;
-----
-
-The handler function for this event would be defined as:
-
-----
-sub sched::sched_wakeup
-{
- my ($event_name, $context, $common_cpu, $common_secs,
- $common_nsecs, $common_pid, $common_comm,
- $comm, $pid, $prio, $success, $target_cpu) = @_;
-}
-----
-
-The handler function takes the form subsystem::event_name.
-
-The $common_* arguments in the handler's argument list are the set of
-arguments passed to all event handlers; some of the fields correspond
-to the common_* fields in the format file, but some are synthesized,
-and some of the common_* fields aren't common enough to to be passed
-to every event as arguments but are available as library functions.
-
-Here's a brief description of each of the invariant event args:
-
- $event_name the name of the event as text
- $context an opaque 'cookie' used in calls back into perf
- $common_cpu the cpu the event occurred on
- $common_secs the secs portion of the event timestamp
- $common_nsecs the nsecs portion of the event timestamp
- $common_pid the pid of the current task
- $common_comm the name of the current process
-
-All of the remaining fields in the event's format file have
-counterparts as handler function arguments of the same name, as can be
-seen in the example above.
-
-The above provides the basics needed to directly access every field of
-every event in a trace, which covers 90% of what you need to know to
-write a useful trace script. The sections below cover the rest.
-
-SCRIPT LAYOUT
--------------
-
-Every perf script Perl script should start by setting up a Perl module
-search path and 'use'ing a few support modules (see module
-descriptions below):
-
-----
- use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
- use lib "./Perf-Trace-Util/lib";
- use Perf::Trace::Core;
- use Perf::Trace::Context;
- use Perf::Trace::Util;
-----
-
-The rest of the script can contain handler functions and support
-functions in any order.
-
-Aside from the event handler functions discussed above, every script
-can implement a set of optional functions:
-
-*trace_begin*, if defined, is called before any event is processed and
-gives scripts a chance to do setup tasks:
-
-----
- sub trace_begin
- {
- }
-----
-
-*trace_end*, if defined, is called after all events have been
- processed and gives scripts a chance to do end-of-script tasks, such
- as display results:
-
-----
-sub trace_end
-{
-}
-----
-
-*trace_unhandled*, if defined, is called after for any event that
- doesn't have a handler explicitly defined for it. The standard set
- of common arguments are passed into it:
-
-----
-sub trace_unhandled
-{
- my ($event_name, $context, $common_cpu, $common_secs,
- $common_nsecs, $common_pid, $common_comm) = @_;
-}
-----
-
-The remaining sections provide descriptions of each of the available
-built-in perf script Perl modules and their associated functions.
-
-AVAILABLE MODULES AND FUNCTIONS
--------------------------------
-
-The following sections describe the functions and variables available
-via the various Perf::Trace::* Perl modules. To use the functions and
-variables from the given module, add the corresponding 'use
-Perf::Trace::XXX' line to your perf script script.
-
-Perf::Trace::Core Module
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-These functions provide some essential functions to user scripts.
-
-The *flag_str* and *symbol_str* functions provide human-readable
-strings for flag and symbolic fields. These correspond to the strings
-and values parsed from the 'print fmt' fields of the event format
-files:
-
- flag_str($event_name, $field_name, $field_value) - returns the string representation corresponding to $field_value for the flag field $field_name of event $event_name
- symbol_str($event_name, $field_name, $field_value) - returns the string representation corresponding to $field_value for the symbolic field $field_name of event $event_name
-
-Perf::Trace::Context Module
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Some of the 'common' fields in the event format file aren't all that
-common, but need to be made accessible to user scripts nonetheless.
-
-Perf::Trace::Context defines a set of functions that can be used to
-access this data in the context of the current event. Each of these
-functions expects a $context variable, which is the same as the
-$context variable passed into every event handler as the second
-argument.
-
- common_pc($context) - returns common_preempt count for the current event
- common_flags($context) - returns common_flags for the current event
- common_lock_depth($context) - returns common_lock_depth for the current event
-
-Perf::Trace::Util Module
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-Various utility functions for use with perf script:
-
- nsecs($secs, $nsecs) - returns total nsecs given secs/nsecs pair
- nsecs_secs($nsecs) - returns whole secs portion given nsecs
- nsecs_nsecs($nsecs) - returns nsecs remainder given nsecs
- nsecs_str($nsecs) - returns printable string in the form secs.nsecs
- avg($total, $n) - returns average given a sum and a total number of values
-
-SEE ALSO
---------
-linkperf:perf-script[1]
diff --git a/tools/perf/Documentation/perf-script-python.txt b/tools/perf/Documentation/perf-script-python.txt
index 27a1cac6fe76..9293057cee8e 100644
--- a/tools/perf/Documentation/perf-script-python.txt
+++ b/tools/perf/Documentation/perf-script-python.txt
@@ -3,676 +3,152 @@ perf-script-python(1)
NAME
----
-perf-script-python - Process trace data with a Python script
+perf-script-python - Process trace data with a Python script using perf module
SYNOPSIS
--------
[verse]
-'perf script' [-s [Python]:script[.py] ]
+'perf script' <script>.py
DESCRIPTION
-----------
-This perf script option is used to process perf script data using perf's
-built-in Python interpreter. It reads and processes the input file and
-displays the results of the trace analysis implemented in the given
-Python script, if any.
+This document describes how to use the `perf` Python module to process
+trace data recorded by `perf record`.
+
+With the removal of embedded Python interpreter from `perf`, scripts
+are now run as standalone Python programs that import the `perf`
+module to access trace data.
A QUICK EXAMPLE
---------------
-This section shows the process, start to finish, of creating a working
-Python script that aggregates and extracts useful information from a
-raw perf script stream. You can avoid reading the rest of this
-document if an example is enough for you; the rest of the document
-provides more details on each step and lists the library functions
-available to script writers.
-
-This example actually details the steps that were used to create the
-'syscall-counts' script you see when you list the available perf script
-scripts via 'perf script -l'. As such, this script also shows how to
-integrate your script into the list of general-purpose 'perf script'
-scripts listed by that command.
+This section shows how to create a simple Python script that reads a
+`perf.data` file and prints event information.
-The syscall-counts script is a simple script, but demonstrates all the
-basic ideas necessary to create a useful script. Here's an example
-of its output (syscall names are not yet supported, they will appear
-as numbers):
+Create a file named `print_events.py` with the following content:
----
-syscall events:
-
-event count
----------------------------------------- -----------
-sys_write 455067
-sys_getdents 4072
-sys_close 3037
-sys_swapoff 1769
-sys_read 923
-sys_sched_setparam 826
-sys_open 331
-sys_newfstat 326
-sys_mmap 217
-sys_munmap 216
-sys_futex 141
-sys_select 102
-sys_poll 84
-sys_setitimer 12
-sys_writev 8
-15 8
-sys_lseek 7
-sys_rt_sigprocmask 6
-sys_wait4 3
-sys_ioctl 3
-sys_set_robust_list 1
-sys_exit 1
-56 1
-sys_access 1
-----
-
-Basically our task is to keep a per-syscall tally that gets updated
-every time a system call occurs in the system. Our script will do
-that, but first we need to record the data that will be processed by
-that script. Theoretically, there are a couple of ways we could do
-that:
+#!/usr/bin/env python3
+import perf
-- we could enable every event under the tracing/events/syscalls
- directory, but this is over 600 syscalls, well beyond the number
- allowable by perf. These individual syscall events will however be
- useful if we want to later use the guidance we get from the
- general-purpose scripts to drill down and get more detail about
- individual syscalls of interest.
+def process_event(sample):
+ print(f"Event: {sample.evsel} on CPU {sample.sample_cpu} at {sample.sample_time}")
-- we can enable the sys_enter and/or sys_exit syscalls found under
- tracing/events/raw_syscalls. These are called for all syscalls; the
- 'id' field can be used to distinguish between individual syscall
- numbers.
-
-For this script, we only need to know that a syscall was entered; we
-don't care how it exited, so we'll use 'perf record' to record only
-the sys_enter events:
+# Open the session with perf.data file
+session = perf.session(perf.data("perf.data"), sample=process_event)
+# Process all events
+session.process_events()
----
-# perf record -a -e raw_syscalls:sys_enter
-^C[ perf record: Woken up 1 times to write data ]
-[ perf record: Captured and wrote 56.545 MB perf.data (~2470503 samples) ]
+Make the script executable:
----
-
-The options basically say to collect data for every syscall event
-system-wide and multiplex the per-cpu output into a single stream.
-That single stream will be recorded in a file in the current directory
-called perf.data.
-
-Once we have a perf.data file containing our data, we can use the -g
-'perf script' option to generate a Python script that will contain a
-callback handler for each event type found in the perf.data trace
-stream (for more details, see the STARTER SCRIPTS section).
-
+$ chmod +x print_events.py
----
-# perf script -g python
-generated Python script: perf-script.py
-
-The output file created also in the current directory is named
-perf-script.py. Here's the file in its entirety:
-
-# perf script event handlers, generated by perf script -g python
-# Licensed under the terms of the GNU GPL License version 2
-
-# The common_* event handler fields are the most useful fields common to
-# all events. They don't necessarily correspond to the 'common_*' fields
-# in the format files. Those fields not available as handler params can
-# be retrieved using Python functions of the form common_*(context).
-# See the perf-script-python Documentation for the list of available functions.
-
-import os
-import sys
-
-sys.path.append(os.environ['PERF_EXEC_PATH'] + \
- '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
-
-from perf_trace_context import *
-from Core import *
-
-def trace_begin():
- print "in trace_begin"
-
-def trace_end():
- print "in trace_end"
-
-def raw_syscalls__sys_enter(event_name, context, common_cpu,
- common_secs, common_nsecs, common_pid, common_comm,
- id, args):
- print_header(event_name, common_cpu, common_secs, common_nsecs,
- common_pid, common_comm)
-
- print "id=%d, args=%s\n" % \
- (id, args),
-
-def trace_unhandled(event_name, context, event_fields_dict):
- print ' '.join(['%s=%s'%(k,str(v))for k,v in sorted(event_fields_dict.items())])
-
-def print_header(event_name, cpu, secs, nsecs, pid, comm):
- print "%-20s %5u %05u.%09u %8u %-20s " % \
- (event_name, cpu, secs, nsecs, pid, comm),
-----
-
-At the top is a comment block followed by some import statements and a
-path append which every perf script script should include.
-
-Following that are a couple generated functions, trace_begin() and
-trace_end(), which are called at the beginning and the end of the
-script respectively (for more details, see the SCRIPT_LAYOUT section
-below).
-
-Following those are the 'event handler' functions generated one for
-every event in the 'perf record' output. The handler functions take
-the form subsystem\__event_name, and contain named parameters, one for
-each field in the event; in this case, there's only one event,
-raw_syscalls__sys_enter(). (see the EVENT HANDLERS section below for
-more info on event handlers).
-
-The final couple of functions are, like the begin and end functions,
-generated for every script. The first, trace_unhandled(), is called
-every time the script finds an event in the perf.data file that
-doesn't correspond to any event handler in the script. This could
-mean either that the record step recorded event types that it wasn't
-really interested in, or the script was run against a trace file that
-doesn't correspond to the script.
-
-The script generated by -g option simply prints a line for each
-event found in the trace stream i.e. it basically just dumps the event
-and its parameter values to stdout. The print_header() function is
-simply a utility function used for that purpose. Let's rename the
-script and run it to see the default output:
+Record some data:
----
-# mv perf-script.py syscall-counts.py
-# perf script -s syscall-counts.py
-
-raw_syscalls__sys_enter 1 00840.847582083 7506 perf id=1, args=
-raw_syscalls__sys_enter 1 00840.847595764 7506 perf id=1, args=
-raw_syscalls__sys_enter 1 00840.847620860 7506 perf id=1, args=
-raw_syscalls__sys_enter 1 00840.847710478 6533 npviewer.bin id=78, args=
-raw_syscalls__sys_enter 1 00840.847719204 6533 npviewer.bin id=142, args=
-raw_syscalls__sys_enter 1 00840.847755445 6533 npviewer.bin id=3, args=
-raw_syscalls__sys_enter 1 00840.847775601 6533 npviewer.bin id=3, args=
-raw_syscalls__sys_enter 1 00840.847781820 6533 npviewer.bin id=3, args=
-.
-.
-.
+$ perf record -a sleep 1
----
-Of course, for this script, we're not interested in printing every
-trace event, but rather aggregating it in a useful way. So we'll get
-rid of everything to do with printing as well as the trace_begin() and
-trace_unhandled() functions, which we won't be using. That leaves us
-with this minimalistic skeleton:
-
+Run the script:
----
-import os
-import sys
-
-sys.path.append(os.environ['PERF_EXEC_PATH'] + \
- '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
-
-from perf_trace_context import *
-from Core import *
-
-def trace_end():
- print "in trace_end"
-
-def raw_syscalls__sys_enter(event_name, context, common_cpu,
- common_secs, common_nsecs, common_pid, common_comm,
- id, args):
+$ perf script print_events.py
----
-In trace_end(), we'll simply print the results, but first we need to
-generate some results to print. To do that we need to have our
-sys_enter() handler do the necessary tallying until all events have
-been counted. A hash table indexed by syscall id is a good way to
-store that information; every time the sys_enter() handler is called,
-we simply increment a count associated with that hash entry indexed by
-that syscall id:
-
+Or run it directly with Python, ensuring `perf.so` is in your `PYTHONPATH`:
----
- syscalls = autodict()
-
- try:
- syscalls[id] += 1
- except TypeError:
- syscalls[id] = 1
+$ PYTHONPATH=/path/to/perf/python python3 print_events.py
----
-The syscalls 'autodict' object is a special kind of Python dictionary
-(implemented in Core.py) that implements Perl's 'autovivifying' hashes
-in Python i.e. with autovivifying hashes, you can assign nested hash
-values without having to go to the trouble of creating intermediate
-levels if they don't exist e.g syscalls[comm][pid][id] = 1 will create
-the intermediate hash levels and finally assign the value 1 to the
-hash entry for 'id' (because the value being assigned isn't a hash
-object itself, the initial value is assigned in the TypeError
-exception. Well, there may be a better way to do this in Python but
-that's what works for now).
-
-Putting that code into the raw_syscalls__sys_enter() handler, we
-effectively end up with a single-level dictionary keyed on syscall id
-and having the counts we've tallied as values.
-
-The print_syscall_totals() function iterates over the entries in the
-dictionary and displays a line for each entry containing the syscall
-name (the dictionary keys contain the syscall ids, which are passed to
-the Util function syscall_name(), which translates the raw syscall
-numbers to the corresponding syscall name strings). The output is
-displayed after all the events in the trace have been processed, by
-calling the print_syscall_totals() function from the trace_end()
-handler called at the end of script processing.
-
-The final script producing the output shown above is shown in its
-entirety below (syscall_name() helper is not yet available, you can
-only deal with id's for now):
-
-----
-import os
-import sys
-
-sys.path.append(os.environ['PERF_EXEC_PATH'] + \
- '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
-
-from perf_trace_context import *
-from Core import *
-from Util import *
-
-syscalls = autodict()
-
-def trace_end():
- print_syscall_totals()
-
-def raw_syscalls__sys_enter(event_name, context, common_cpu,
- common_secs, common_nsecs, common_pid, common_comm,
- id, args):
- try:
- syscalls[id] += 1
- except TypeError:
- syscalls[id] = 1
-
-def print_syscall_totals():
- if for_comm is not None:
- print "\nsyscall events for %s:\n\n" % (for_comm),
- else:
- print "\nsyscall events:\n\n",
-
- print "%-40s %10s\n" % ("event", "count"),
- print "%-40s %10s\n" % ("----------------------------------------", \
- "-----------"),
-
- for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \
- reverse = True):
- print "%-40s %10d\n" % (syscall_name(id), val),
-----
-
-The script can be run just as before:
-
- # perf script -s syscall-counts.py
-
-So those are the essential steps in writing and running a script. The
-process can be generalized to any tracepoint or set of tracepoints
-you're interested in - basically find the tracepoint(s) you're
-interested in by looking at the list of available events shown by
-'perf list' and/or look in /sys/kernel/tracing/events/ for
-detailed event and field info, record the corresponding trace data
-using 'perf record', passing it the list of interesting events,
-generate a skeleton script using 'perf script -g python' and modify the
-code to aggregate and display it for your particular needs.
-
-After you've done that you may end up with a general-purpose script
-that you want to keep around and have available for future use. By
-writing a couple of very simple shell scripts and putting them in the
-right place, you can have your script listed alongside the other
-scripts listed by the 'perf script -l' command e.g.:
-
-----
-# perf script -l
-List of available trace scripts:
- wakeup-latency system-wide min/max/avg wakeup latency
- rw-by-file <comm> r/w activity for a program, by file
- rw-by-pid system-wide r/w activity
-----
-
-A nice side effect of doing this is that you also then capture the
-probably lengthy 'perf record' command needed to record the events for
-the script.
-
-To have the script appear as a 'built-in' script, you write two simple
-scripts, one for recording and one for 'reporting'.
-
-The 'record' script is a shell script with the same base name as your
-script, but with -record appended. The shell script should be put
-into the perf/scripts/python/bin directory in the kernel source tree.
-In that script, you write the 'perf record' command-line needed for
-your script:
-
-----
-# cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-record
-
-#!/bin/bash
-perf record -a -e raw_syscalls:sys_enter
-----
-
-The 'report' script is also a shell script with the same base name as
-your script, but with -report appended. It should also be located in
-the perf/scripts/python/bin directory. In that script, you write the
-'perf script -s' command-line needed for running your script:
-
-----
-# cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-report
-
-#!/bin/bash
-# description: system-wide syscall counts
-perf script -s ~/libexec/perf-core/scripts/python/syscall-counts.py
-----
-
-Note that the location of the Python script given in the shell script
-is in the libexec/perf-core/scripts/python directory - this is where
-the script will be copied by 'make install' when you install perf.
-For the installation to install your script there, your script needs
-to be located in the perf/scripts/python directory in the kernel
-source tree:
-
-----
-# ls -al kernel-source/tools/perf/scripts/python
-total 32
-drwxr-xr-x 4 trz trz 4096 2010-01-26 22:30 .
-drwxr-xr-x 4 trz trz 4096 2010-01-26 22:29 ..
-drwxr-xr-x 2 trz trz 4096 2010-01-26 22:29 bin
--rw-r--r-- 1 trz trz 2548 2010-01-26 22:29 check-perf-script.py
-drwxr-xr-x 3 trz trz 4096 2010-01-26 22:49 Perf-Trace-Util
--rw-r--r-- 1 trz trz 1462 2010-01-26 22:30 syscall-counts.py
-----
-
-Once you've done that (don't forget to do a new 'make install',
-otherwise your script won't show up at run-time), 'perf script -l'
-should show a new entry for your script:
-
-----
-# perf script -l
-List of available trace scripts:
- wakeup-latency system-wide min/max/avg wakeup latency
- rw-by-file <comm> r/w activity for a program, by file
- rw-by-pid system-wide r/w activity
- syscall-counts system-wide syscall counts
-----
-
-You can now perform the record step via 'perf script record':
-
- # perf script record syscall-counts
-
-and display the output using 'perf script report':
-
- # perf script report syscall-counts
-
-STARTER SCRIPTS
+THE PERF MODULE
---------------
-You can quickly get started writing a script for a particular set of
-trace data by generating a skeleton script using 'perf script -g
-python' in the same directory as an existing perf.data trace file.
-That will generate a starter script containing a handler for each of
-the event types in the trace file; it simply prints every available
-field for each event in the trace file.
-
-You can also look at the existing scripts in
-~/libexec/perf-core/scripts/python for typical examples showing how to
-do basic things like aggregate event data, print results, etc. Also,
-the check-perf-script.py script, while not interesting for its results,
-attempts to exercise all of the main scripting features.
-
-EVENT HANDLERS
---------------
-
-When perf script is invoked using a trace script, a user-defined
-'handler function' is called for each event in the trace. If there's
-no handler function defined for a given event type, the event is
-ignored (or passed to a 'trace_unhandled' function, see below) and the
-next event is processed.
-
-Most of the event's field values are passed as arguments to the
-handler function; some of the less common ones aren't - those are
-available as calls back into the perf executable (see below).
-
-As an example, the following perf record command can be used to record
-all sched_wakeup events in the system:
-
- # perf record -a -e sched:sched_wakeup
-
-Traces meant to be processed using a script should be recorded with
-the above option: -a to enable system-wide collection.
-
-The format file for the sched_wakeup event defines the following fields
-(see /sys/kernel/tracing/events/sched/sched_wakeup/format):
-
-----
- format:
- field:unsigned short common_type;
- field:unsigned char common_flags;
- field:unsigned char common_preempt_count;
- field:int common_pid;
-
- field:char comm[TASK_COMM_LEN];
- field:pid_t pid;
- field:int prio;
- field:int success;
- field:int target_cpu;
-----
-
-The handler function for this event would be defined as:
-
-----
-def sched__sched_wakeup(event_name, context, common_cpu, common_secs,
- common_nsecs, common_pid, common_comm,
- comm, pid, prio, success, target_cpu):
- pass
-----
-
-The handler function takes the form subsystem__event_name.
-
-The common_* arguments in the handler's argument list are the set of
-arguments passed to all event handlers; some of the fields correspond
-to the common_* fields in the format file, but some are synthesized,
-and some of the common_* fields aren't common enough to to be passed
-to every event as arguments but are available as library functions.
-
-Here's a brief description of each of the invariant event args:
-
- event_name the name of the event as text
- context an opaque 'cookie' used in calls back into perf
- common_cpu the cpu the event occurred on
- common_secs the secs portion of the event timestamp
- common_nsecs the nsecs portion of the event timestamp
- common_pid the pid of the current task
- common_comm the name of the current process
-
-All of the remaining fields in the event's format file have
-counterparts as handler function arguments of the same name, as can be
-seen in the example above.
-
-The above provides the basics needed to directly access every field of
-every event in a trace, which covers 90% of what you need to know to
-write a useful trace script. The sections below cover the rest.
-
-SCRIPT LAYOUT
--------------
-
-Every perf script Python script should start by setting up a Python
-module search path and 'import'ing a few support modules (see module
-descriptions below):
-
-----
- import os
- import sys
-
- sys.path.append(os.environ['PERF_EXEC_PATH'] + \
- '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
-
- from perf_trace_context import *
- from Core import *
-----
-
-The rest of the script can contain handler functions and support
-functions in any order.
-
-Aside from the event handler functions discussed above, every script
-can implement a set of optional functions:
-
-*trace_begin*, if defined, is called before any event is processed and
-gives scripts a chance to do setup tasks:
-
-----
-def trace_begin():
- pass
-----
-
-*trace_end*, if defined, is called after all events have been
- processed and gives scripts a chance to do end-of-script tasks, such
- as display results:
-
-----
-def trace_end():
- pass
-----
-
-*trace_unhandled*, if defined, is called after for any event that
- doesn't have a handler explicitly defined for it. The standard set
- of common arguments are passed into it:
-
-----
-def trace_unhandled(event_name, context, event_fields_dict):
- pass
-----
-
-*process_event*, if defined, is called for any non-tracepoint event
-
-----
-def process_event(param_dict):
- pass
-----
-
-*context_switch*, if defined, is called for any context switch
-
-----
-def context_switch(ts, cpu, pid, tid, np_pid, np_tid, machine_pid, out, out_preempt, *x):
- pass
-----
-
-*auxtrace_error*, if defined, is called for any AUX area tracing error
-
-----
-def auxtrace_error(typ, code, cpu, pid, tid, ip, ts, msg, cpumode, *x):
- pass
-----
-
-The remaining sections provide descriptions of each of the available
-built-in perf script Python modules and their associated functions.
-
-AVAILABLE MODULES AND FUNCTIONS
--------------------------------
-
-The following sections describe the functions and variables available
-via the various perf script Python modules. To use the functions and
-variables from the given module, add the corresponding 'from XXXX
-import' line to your perf script script.
-
-Core.py Module
-~~~~~~~~~~~~~~
+The `perf` module provides several classes and functions to interact
+with trace data.
-These functions provide some essential functions to user scripts.
+### Module Functions
-The *flag_str* and *symbol_str* functions provide human-readable
-strings for flag and symbolic fields. These correspond to the strings
-and values parsed from the 'print fmt' fields of the event format
-files:
+- `config_get(name)`: Get a perf config value.
+- `metrics()`: Returns a list of metrics represented as string values in dictionaries.
+- `tracepoint(subsystem, name)`: Get tracepoint config.
+- `parse_events(string)`: Parse a string of events and return an `evlist`.
+- `parse_metrics(string, pmu=None)`: Parse a string of metrics or metric groups and return an `evlist`.
+- `pmus()`: Returns a sequence of PMUs.
+- `syscall_name(num)`: Turns a syscall number to a string.
+- `syscall_id(name)`: Turns a syscall name to a number.
- flag_str(event_name, field_name, field_value) - returns the string representation corresponding to field_value for the flag field field_name of event event_name
- symbol_str(event_name, field_name, field_value) - returns the string representation corresponding to field_value for the symbolic field field_name of event event_name
+### `perf.pmu`
-The *autodict* function returns a special kind of Python
-dictionary that implements Perl's 'autovivifying' hashes in Python
-i.e. with autovivifying hashes, you can assign nested hash values
-without having to go to the trouble of creating intermediate levels if
-they don't exist.
+Represents a Performance Monitoring Unit.
- autodict() - returns an autovivifying dictionary instance
+- `events()`: Returns a sequence of events encoded as dictionaries.
+- `name()`: Name of the PMU including suffixes.
+### `perf.evlist`
-perf_trace_context Module
-~~~~~~~~~~~~~~~~~~~~~~~~~
+Represents a list of event selectors.
-Some of the 'common' fields in the event format file aren't all that
-common, but need to be made accessible to user scripts nonetheless.
+- `all_cpus()`: CPU map union of all evsel CPU maps.
+- `metrics()`: List of metric names within the evlist.
+- `compute_metric(name, cpu, thread)`: Compute metric for given name, cpu and thread.
+- `mmap()`: mmap the file descriptor table.
+- `open()`: open the file descriptors.
+- `close()`: close the file descriptors.
+- `poll()`: poll the file descriptor table.
+- `get_pollfd()`: get the poll file descriptor table.
+- `add(evsel)`: adds an event selector to the list.
+- `read_on_cpu(cpu)`: reads an event.
+- `config()`: Apply default record options to the evlist.
+- `disable()`: Disable the evsels in the evlist.
+- `enable()`: Enable the evsels in the evlist.
-perf_trace_context defines a set of functions that can be used to
-access this data in the context of the current event. Each of these
-functions expects a context variable, which is the same as the
-context variable passed into every tracepoint event handler as the second
-argument. For non-tracepoint events, the context variable is also present
-as perf_trace_context.perf_script_context .
+### `perf.evsel`
- common_pc(context) - returns common_preempt count for the current event
- common_flags(context) - returns common_flags for the current event
- common_lock_depth(context) - returns common_lock_depth for the current event
- perf_sample_insn(context) - returns the machine code instruction
- perf_set_itrace_options(context, itrace_options) - set --itrace options if they have not been set already
- perf_sample_srcline(context) - returns source_file_name, line_number
- perf_sample_srccode(context) - returns source_file_name, line_number, source_line
- perf_config_get(config_name) - returns the value of the named config item, or None if unset
+Represents an event selector.
-Util.py Module
-~~~~~~~~~~~~~~
+- `open()`: open the event selector file descriptor table.
+- `cpus()`: CPUs the event is to be used with.
+- `threads()`: threads the event is to be used with.
+- `read(cpu, thread)`: read counters. Returns a count object with `val`, `ena`, and `run` attributes.
-Various utility functions for use with perf script:
+### `perf.session`
- nsecs(secs, nsecs) - returns total nsecs given secs/nsecs pair
- nsecs_secs(nsecs) - returns whole secs portion given nsecs
- nsecs_nsecs(nsecs) - returns nsecs remainder given nsecs
- nsecs_str(nsecs) - returns printable string in the form secs.nsecs
- avg(total, n) - returns average given a sum and a total number of values
+Manages a trace session.
-SUPPORTED FIELDS
-----------------
+- `__init__(data, sample=None)`: Creates a new session. `data` is a `perf.data` object. `sample` is a callback function called for each sample event.
+- `process_events()`: Reads the trace data and calls the sample callback for each event.
-Currently supported fields:
+### `perf.data`
-ev_name, comm, id, stream_id, pid, tid, cpu, ip, time, period, phys_addr,
-addr, symbol, symoff, dso, time_enabled, time_running, values, callchain,
-brstack, brstacksym, datasrc, datasrc_decode, iregs, uregs,
-weight, transaction, raw_buf, attr, cpumode.
+Represents a trace file.
-Fields that may also be present:
+- `__init__(filename, mode=perf.DATA_MODE_READ)`: Opens a trace file.
- flags - sample flags
- flags_disp - sample flags display
- insn_cnt - instruction count for determining instructions-per-cycle (IPC)
- cyc_cnt - cycle count for determining IPC
- addr_correlates_sym - addr can correlate to a symbol
- addr_dso - addr dso
- addr_symbol - addr symbol
- addr_symoff - addr symbol offset
+### Sample Object
-Some fields have sub items:
+Passed to the callback function in `perf.session`.
-brstack:
- from, to, from_dsoname, to_dsoname, mispred,
- predicted, in_tx, abort, cycles.
+- `evsel`: The event selector (name of the event).
+- `sample_cpu`: The CPU on which the event occurred.
+- `sample_time`: The timestamp of the event.
+- `sample_pid`: The PID of the process.
+- `sample_tid`: The TID of the thread.
+- `raw_buf`: Raw buffer containing event specific data.
-brstacksym:
- items: from, to, pred, in_tx, abort (converted string)
+COUNTER AND METRIC APIS
+-----------------------
-For example,
-We can use this code to print brstack "from", "to", "cycles".
+The following APIs are used in `tools/perf/python/ilist.py` for
+interactive listing and reading of counters and metrics:
-if 'brstack' in dict:
- for entry in dict['brstack']:
- print "from %s, to %s, cycles %s" % (entry["from"], entry["to"], entry["cycles"])
+- `perf.pmus()`: Used to get all available PMUs.
+- `pmu.events()`: Used to get all events for a specific PMU.
+- `perf.metrics()`: Used to get all available metrics.
+- `perf.parse_metrics(metric_name, pmu)`: Used to parse a metric and get an `evlist`.
+- `evlist.compute_metric(metric_name, cpu, thread)`: Used to compute a metric value for a specific CPU and thread.
+- `evsel.read(cpu, thread)`: Used to read raw counter values.
SEE ALSO
--------
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt
index 200ea25891d8..93ed1ea704c9 100644
--- a/tools/perf/Documentation/perf-script.txt
+++ b/tools/perf/Documentation/perf-script.txt
@@ -9,10 +9,7 @@ SYNOPSIS
--------
[verse]
'perf script' [<options>]
-'perf script' [<options>] record <script> [<record-options>] <command>
-'perf script' [<options>] report <script> [script-args]
-'perf script' [<options>] <script> <required-script-args> [<record-options>] <command>
-'perf script' [<options>] <top-script> [script-args]
+'perf script' [<options>] <script> [script-args]
DESCRIPTION
-----------
@@ -23,52 +20,9 @@ There are several variants of perf script:
'perf script' to see a detailed trace of the workload that was
recorded.
- You can also run a set of pre-canned scripts that aggregate and
- summarize the raw trace data in various ways (the list of scripts is
- available via 'perf script -l'). The following variants allow you to
- record and run those scripts:
-
- 'perf script record <script> <command>' to record the events required
- for 'perf script report'. <script> is the name displayed in the
- output of 'perf script --list' i.e. the actual script name minus any
- language extension. If <command> is not specified, the events are
- recorded using the -a (system-wide) 'perf record' option.
-
- 'perf script report <script> [args]' to run and display the results
- of <script>. <script> is the name displayed in the output of 'perf
- script --list' i.e. the actual script name minus any language
- extension. The perf.data output from a previous run of 'perf script
- record <script>' is used and should be present for this command to
- succeed. [args] refers to the (mainly optional) args expected by
- the script.
-
- 'perf script <script> <required-script-args> <command>' to both
- record the events required for <script> and to run the <script>
- using 'live-mode' i.e. without writing anything to disk. <script>
- is the name displayed in the output of 'perf script --list' i.e. the
- actual script name minus any language extension. If <command> is
- not specified, the events are recorded using the -a (system-wide)
- 'perf record' option. If <script> has any required args, they
- should be specified before <command>. This mode doesn't allow for
- optional script args to be specified; if optional script args are
- desired, they can be specified using separate 'perf script record'
- and 'perf script report' commands, with the stdout of the record step
- piped to the stdin of the report script, using the '-o -' and '-i -'
- options of the corresponding commands.
-
- 'perf script <top-script>' to both record the events required for
- <top-script> and to run the <top-script> using 'live-mode'
- i.e. without writing anything to disk. <top-script> is the name
- displayed in the output of 'perf script --list' i.e. the actual
- script name minus any language extension; a <top-script> is defined
- as any script name ending with the string 'top'.
-
- [<record-options>] can be passed to the record steps of 'perf script
- record' and 'live-mode' variants; this isn't possible however for
- <top-script> 'live-mode' or 'perf script report' variants.
-
- See the 'SEE ALSO' section for links to language-specific
- information on how to write and run your own trace scripts.
+ You can also run standalone scripts that aggregate and summarize the
+ raw trace data in various ways (the list of scripts is available via
+ 'perf script -l').
OPTIONS
-------
@@ -90,18 +44,7 @@ OPTIONS
--list=::
Display a list of available trace scripts.
--s ['lang']::
---script=::
- Process trace data with the given script ([lang]:script[.ext]).
- If the string 'lang' is specified in place of a script name, a
- list of supported languages will be displayed instead.
--g::
---gen-script=::
- Generate a starter script. If a language is given then the
- script is named perf-script.[ext] according to the
- language. If a file path is given then python is used for
- files ending '.py' and perl used for files ending '.pl'.
--dlfilter=<file>::
Filter sample events using the given shared object file.
@@ -543,6 +486,5 @@ include::guest-files.txt[]
SEE ALSO
--------
-linkperf:perf-record[1], linkperf:perf-script-perl[1],
-linkperf:perf-script-python[1], linkperf:perf-intel-pt[1],
-linkperf:perf-dlfilter[1]
+linkperf:perf-record[1], linkperf:perf-script-python[1],
+linkperf:perf-intel-pt[1], linkperf:perf-dlfilter[1]
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* Re: [PATCH 1/2] dt-bindings: remoteproc: xlnx: add auto boot feature
From: Krzysztof Kozlowski @ 2026-04-24 16:53 UTC (permalink / raw)
To: tanmay.shah
Cc: andersson, mathieu.poirier, robh, krzk+dt, conor+dt, michal.simek,
ben.levinsky, linux-remoteproc, devicetree, linux-arm-kernel,
linux-kernel
In-Reply-To: <faef3c54-2292-4470-be6a-4c347ca65453@amd.com>
On 23/04/2026 19:59, Shah, Tanmay wrote:
> Ack, I will rename it to xlnx,auto-boot.
>
>>>
>>>>> + type: boolean
>>>>> + description: remote core is either already running or ready to boot
>>>>
>>>> And why is this property of a board?
>>>>
>>>
>>> Not sure what indicates it is? The property is under remoteproc child
>>> device that is SOC level property. Remote core is on same SOC wher linux
>>> core is running.
>>
>> So it is implied by SoC compatible? Please provide some arguments why it
>> cannot be implied by the SoC compatible. I gave you one way out, but if
>> you disagree then no problem.
>>
>
> So on some SoC, the bootloader supports loading and starting of the
> remote processor. But it is totally user's choice. User can choose to
> load & start one core of a cluster via bootloader and leave another core
> powered-off.
> That is why it is not possible to decide based on SoC compatible.
OK. The problem is that "user" is a bit vague and usually user choice
goes to user-space.
The property will be set or unset for ALL of given boards. So all of the
DTS->DTB. That's why it should be clear why all such boards should
behave like you described. If this is truly user, as in user-space,
choice, then DT is not the way.
>
> If we don't want to make it a device-tree property then I can implement
> in a different way. New way will detect if the remote is running or not
> via EMMI/SCMI call to the firmware, and take a decision based on that.
> If this new way works, then I don't think we need auto-boot property at all.
>
> Let me know your thoughts.
This works for me and solves my questions from DT point of view, but I
cannot judge whether this makes sense for you.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [RFC PATCH v3 2/4] security: ima: call ima_init() again at late_initcall_sync for defered TPM
From: Yeoreum Yun @ 2026-04-24 16:55 UTC (permalink / raw)
To: Jonathan McDowell
Cc: linux-security-module, linux-kernel, linux-integrity,
linux-arm-kernel, kvmarm, paul, jmorris, serge, zohar,
roberto.sassu, dmitry.kasatkin, eric.snowberg, jarkko, jgg,
sudeep.holla, maz, oupton, joey.gouly, suzuki.poulose, yuzenghui,
catalin.marinas, will, noodles, sebastianene
In-Reply-To: <5552c20c6d6d2ae3bbb6b35124af5d98d2f79163.1777036497.git.noodles@meta.com>
> From: Jonathan McDowell <noodles@meta.com>
>
> The Linux IMA (Integrity Measurement Architecture) subsystem used for
> secure boot, file integrity, or remote attestation cannot be a loadable
> module for few reasons listed below:
>
> o Boot-Time Integrity: IMA’s main role is to measure and appraise files
> before they are used. This includes measuring critical system files
> during early boot (e.g., init, init scripts, login binaries). If IMA
> were a module, it would be loaded too late to cover those.
>
> o TPM Dependency: IMA integrates tightly with the TPM to record
> measurements into PCRs. The TPM must be initialized early (ideally
> before init_ima()), which aligns with IMA being built-in.
>
> o Security Model: IMA is part of a Trusted Computing Base (TCB). Making
> it a module would weaken the security model, as a potentially
> compromised system could delay or tamper with its initialization.
>
> IMA must be built-in to ensure it starts measuring from the earliest
> possible point in boot which inturn implies TPM must be initialised and
> ready to use before IMA.
>
> Unfortunately some TPM drivers (such as Arm FF-A, or SPI attached TPM
> devices) are not reliably available during the initcall_late stage,
> resulting in a log error:
>
> ima: No TPM chip found, activating TPM-bypass!
>
> and no measurements into the TPM by IMA. We can avoid this by doing IMA
> init in the initcall_late_sync stage, after the drivers have completed
> their init + registration.
>
> Rather than do this everywhere, and needlessly delay the initialisation
> of IMA when there is no need to do so, we continue to try to initialise
> at the earlier stage, only deferring to the later point if the TPM is
> not available yet.
>
> Signed-off-by: Jonathan McDowell <noodles@meta.com>
> ---
> security/integrity/ima/ima.h | 3 +-
> security/integrity/ima/ima_init.c | 25 ++++++++-------
> security/integrity/ima/ima_main.c | 37 ++++++++++++++++++++---
> security/integrity/ima/ima_template_lib.c | 3 +-
> 4 files changed, 50 insertions(+), 18 deletions(-)
>
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index 89ebe98ffc5e..b3677b403a5a 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -65,6 +65,7 @@ extern struct ima_algo_desc *ima_algo_array __ro_after_init;
> extern int ima_appraise;
> extern struct tpm_chip *ima_tpm_chip;
> extern const char boot_aggregate_name[];
> +extern const char boot_aggregate_late_name[];
>
> /* IMA event related data */
> struct ima_event_data {
> @@ -257,7 +258,7 @@ static inline void ima_measure_kexec_event(const char *event_name) {}
> extern bool ima_canonical_fmt;
>
> /* Internal IMA function definitions */
> -int ima_init(void);
> +int ima_init_core(bool late);
> int ima_fs_init(void);
> int ima_add_template_entry(struct ima_template_entry *entry, int violation,
> const char *op, struct inode *inode,
> diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
> index a2f34f2d8ad7..5f335834a9bb 100644
> --- a/security/integrity/ima/ima_init.c
> +++ b/security/integrity/ima/ima_init.c
> @@ -22,6 +22,7 @@
>
> /* name for boot aggregate entry */
> const char boot_aggregate_name[] = "boot_aggregate";
> +const char boot_aggregate_late_name[] = "boot_aggregate_late";
> struct tpm_chip *ima_tpm_chip;
>
> /* Add the boot aggregate to the IMA measurement list and extend
> @@ -39,17 +40,17 @@ struct tpm_chip *ima_tpm_chip;
> * a different value.) Violations add a zero entry to the measurement
> * list and extend the aggregate PCR value with ff...ff's.
> */
> -static int __init ima_add_boot_aggregate(void)
> +static int __init ima_add_boot_aggregate(bool late)
> {
> static const char op[] = "add_boot_aggregate";
> const char *audit_cause = "ENOMEM";
> struct ima_template_entry *entry;
> struct ima_iint_cache tmp_iint, *iint = &tmp_iint;
> - struct ima_event_data event_data = { .iint = iint,
> - .filename = boot_aggregate_name };
> + struct ima_event_data event_data = { .iint = iint };
> struct ima_max_digest_data hash;
> struct ima_digest_data *hash_hdr = container_of(&hash.hdr,
> struct ima_digest_data, hdr);
> + const char *filename;
> int result = -ENOMEM;
> int violation = 0;
>
> @@ -59,6 +60,12 @@ static int __init ima_add_boot_aggregate(void)
> iint->ima_hash->algo = ima_hash_algo;
> iint->ima_hash->length = hash_digest_size[ima_hash_algo];
>
> + if (late)
> + filename = boot_aggregate_late_name;
> + else
> + filename = boot_aggregate_name;
> + event_data.filename = filename;
> +
> /*
> * With TPM 2.0 hash agility, TPM chips could support multiple TPM
> * PCR banks, allowing firmware to configure and enable different
> @@ -86,7 +93,7 @@ static int __init ima_add_boot_aggregate(void)
> }
>
> result = ima_store_template(entry, violation, NULL,
> - boot_aggregate_name,
> + filename,
> CONFIG_IMA_MEASURE_PCR_IDX);
> if (result < 0) {
> ima_free_template_entry(entry);
> @@ -95,7 +102,7 @@ static int __init ima_add_boot_aggregate(void)
> }
> return 0;
> err_out:
> - integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, boot_aggregate_name, op,
> + integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, filename, op,
> audit_cause, result, 0);
> return result;
> }
> @@ -115,14 +122,10 @@ void __init ima_load_x509(void)
> }
> #endif
>
> -int __init ima_init(void)
> +int __init ima_init_core(bool late)
> {
> int rc;
>
> - ima_tpm_chip = tpm_default_chip();
> - if (!ima_tpm_chip)
> - pr_info("No TPM chip found, activating TPM-bypass!\n");
> -
> rc = integrity_init_keyring(INTEGRITY_KEYRING_IMA);
> if (rc)
> return rc;
> @@ -140,7 +143,7 @@ int __init ima_init(void)
> rc = ima_init_digests();
> if (rc != 0)
> return rc;
> - rc = ima_add_boot_aggregate(); /* boot aggregate must be first entry */
> + rc = ima_add_boot_aggregate(late); /* boot aggregate must be first entry */
> if (rc != 0)
> return rc;
>
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> index 1d6229b156fb..0b93a286c0d3 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -1237,7 +1237,7 @@ static int ima_kernel_module_request(char *kmod_name)
>
> #endif /* CONFIG_INTEGRITY_ASYMMETRIC_KEYS */
>
> -static int __init init_ima(void)
> +static int __init init_ima(bool late)
> {
> int error;
>
> @@ -1247,10 +1247,26 @@ static int __init init_ima(void)
> return 0;
> }
>
> + /*
> + * If we found the TPM during our first attempt, or we know there's no
> + * TPM, nothing further to do
> + */
> + if (late && (ima_tpm_chip || !IS_ENABLED(CONFIG_TCG_TPM)))
> + return 0;
> +
> + ima_tpm_chip = tpm_default_chip();
> + if (!ima_tpm_chip && !late && IS_ENABLED(CONFIG_TCG_TPM)) {
> + pr_debug("TPM not available, will try later\n");
> + return -EPROBE_DEFER;
> + }
> +
> + if (!ima_tpm_chip)
> + pr_info("No TPM chip found, activating TPM-bypass!\n");
> +
> ima_appraise_parse_cmdline();
> ima_init_template_list();
> hash_setup(CONFIG_IMA_DEFAULT_HASH);
> - error = ima_init();
> + error = ima_init_core(late);
>
> if (error && strcmp(hash_algo_name[ima_hash_algo],
> CONFIG_IMA_DEFAULT_HASH) != 0) {
> @@ -1258,7 +1274,7 @@ static int __init init_ima(void)
> hash_algo_name[ima_hash_algo], CONFIG_IMA_DEFAULT_HASH);
> hash_setup_done = 0;
> hash_setup(CONFIG_IMA_DEFAULT_HASH);
> - error = ima_init();
> + error = ima_init_core(late);
> }
>
> if (error)
> @@ -1274,6 +1290,16 @@ static int __init init_ima(void)
> return error;
> }
>
> +static int __init init_ima_late(void)
> +{
> + return init_ima(false);
> +}
> +
> +static int __init init_ima_late_sync(void)
> +{
> + return init_ima(true);
> +}
> +
> static struct security_hook_list ima_hooks[] __ro_after_init = {
> LSM_HOOK_INIT(bprm_check_security, ima_bprm_check),
> LSM_HOOK_INIT(bprm_creds_for_exec, ima_bprm_creds_for_exec),
> @@ -1319,6 +1345,7 @@ DEFINE_LSM(ima) = {
> .init = init_ima_lsm,
> .order = LSM_ORDER_LAST,
> .blobs = &ima_blob_sizes,
> - /* Start IMA after the TPM is available */
> - .initcall_late = init_ima,
> + /* Ensure we start IMA after the TPM is available */
> + .initcall_late = init_ima_late,
> + .initcall_late_sync = init_ima_late_sync,
> };
> diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
> index 0e627eac9c33..8a89236f926c 100644
> --- a/security/integrity/ima/ima_template_lib.c
> +++ b/security/integrity/ima/ima_template_lib.c
> @@ -363,7 +363,8 @@ int ima_eventdigest_init(struct ima_event_data *event_data,
> goto out;
> }
>
> - if ((const char *)event_data->filename == boot_aggregate_name) {
> + if ((const char *)event_data->filename == boot_aggregate_name ||
> + (const char *)event_data->filename == boot_aggregate_late_name) {
> if (ima_tpm_chip) {
> hash.hdr.algo = HASH_ALGO_SHA1;
> result = ima_calc_boot_aggregate(hash_hdr);
> --
> 2.53.0
>
This looks good to me. Feel free to add:
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Thanks!
--
Sincerely,
Yeoreum Yun
^ permalink raw reply
* Re: [PATCH rc v2 1/5] iommu/arm-smmu-v3: Add arm_smmu_adopt_strtab() for kdump
From: Jason Gunthorpe @ 2026-04-24 16:56 UTC (permalink / raw)
To: Nicolin Chen
Cc: will, robin.murphy, kevin.tian, joro, praan, baolu.lu,
miko.lenczewski, smostafa, linux-arm-kernel, iommu, linux-kernel,
stable, jamien
In-Reply-To: <af5fb880e771bc31ba42644ae5570e1fa208217a.1776286352.git.nicolinc@nvidia.com>
On Wed, Apr 15, 2026 at 02:17:36PM -0700, Nicolin Chen wrote:
> +static int arm_smmu_adopt_strtab_2lvl(struct arm_smmu_device *smmu, u32 cfg_reg,
> + dma_addr_t dma)
> +{
> + u32 log2size = FIELD_GET(STRTAB_BASE_CFG_LOG2SIZE, cfg_reg);
> + u32 split = FIELD_GET(STRTAB_BASE_CFG_SPLIT, cfg_reg);
> + struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
> + u32 num_l1_ents;
> + int i;
> +
> + if (log2size < split) {
> + dev_err(smmu->dev, "kdump: invalid log2size %u < split %u\n",
> + log2size, split);
> + return -EINVAL;
> + }
> +
> + if (split != STRTAB_SPLIT) {
> + dev_err(smmu->dev,
> + "kdump: unsupported STRTAB_SPLIT %u (expected %u)\n",
> + split, STRTAB_SPLIT);
> + return -EINVAL;
> + }
> +
> + num_l1_ents = 1 << (log2size - split);
> + cfg->l2.l1_dma = dma;
> + cfg->l2.num_l1_ents = num_l1_ents;
> + cfg->l2.l1tab = devm_memremap(
> + smmu->dev, dma, num_l1_ents * sizeof(struct arm_smmu_strtab_l1),
> + MEMREMAP_WB);
WB shouldn't be unconditional? If the SMMU is working non-coherently
we need to map it NC. Same remark everwhere
Jason
^ permalink raw reply
* Re: [PATCH rc v2 2/5] iommu/arm-smmu-v3: Implement is_attach_deferred() for kdump
From: Jason Gunthorpe @ 2026-04-24 16:59 UTC (permalink / raw)
To: Nicolin Chen
Cc: will, robin.murphy, kevin.tian, joro, praan, baolu.lu,
miko.lenczewski, smostafa, linux-arm-kernel, iommu, linux-kernel,
stable, jamien
In-Reply-To: <7637d66c0f6c1fb16da4b5c9c4cec71752cf4d23.1776286352.git.nicolinc@nvidia.com>
On Wed, Apr 15, 2026 at 02:17:37PM -0700, Nicolin Chen wrote:
> +static bool arm_smmu_is_attach_deferred(struct device *dev)
> +{
> + struct arm_smmu_master *master = dev_iommu_priv_get(dev);
> + struct arm_smmu_device *smmu = master->smmu;
> + int i;
> +
> + if (!(smmu->options & ARM_SMMU_OPT_KDUMP))
> + return false;
> +
> + for (i = 0; i < master->num_streams; i++) {
> + u32 sid = master->streams[i].id;
> + struct arm_smmu_ste *step;
> +
> + /* Guard against unpopulated L2 entries in the adopted table */
> + if ((smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) &&
> + !smmu->strtab_cfg.l2.l2ptrs[arm_smmu_strtab_l1_idx(sid)])
> + continue;
This can probably just call arm_smmu_init_sid_strtab()
I think it is OK to allocate another level 2 here and it also has
protections for SID out of range..
Jason
^ permalink raw reply
* Re: [PATCH 15/15] arm64: dts: ti: beagley-ai: Enable HDMI display and audio
From: Tomi Valkeinen @ 2026-04-24 17:04 UTC (permalink / raw)
To: Robert Nelson
Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Lee Jones, Aradhya Bhatia, Nishanth Menon, Vignesh Raghavendra,
Swamil Jain, Devarsh Thakkar, Louis Chauvet, devicetree,
dri-devel, linux-kernel, linux-arm-kernel, Andrew Davis
In-Reply-To: <CAOCHtYjJgqomKreDxLUiVbUtpeh36f0bL-jA6P-6rsoJrweshQ@mail.gmail.com>
Hi,
On 24/04/2026 19:16, Robert Nelson wrote:
> On Mon, Apr 20, 2026 at 8:04 AM Tomi Valkeinen
> <tomi.valkeinen@ideasonboard.com> wrote:
>>
>> From: Andrew Davis <afd@ti.com>
>>
>> Enable HDMI support for BeagleY-AI platform. The display controller used is
>> TIDSS and the HDMI bridge used is IT66122.
>>
>> Based on DT by: Robert Nelson <robertcnelson@gmail.com>
>> Signed-off-by: Andrew Davis <afd@ti.com>
>> Signed-off-by: Swamil Jain <s-jain1@ti.com>
>> [tomi.valkeinen: cosmetic fixes]
>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
>
> Tested-by: Robert Nelson <robertcnelson@gmail.com>
>
> Thank you for getting the display back end working on j722s family!
What displays did you test? I don't have OLDI panel...
Tomi
^ permalink raw reply
* Re: [PATCH v12 1/2] media: dt-bindings: nxp,imx8-isi: Drop fsl,blk-ctrl requirement for i.MX8ULP
From: Conor Dooley @ 2026-04-24 17:04 UTC (permalink / raw)
To: Guoniu Zhou
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Laurent Pinchart,
Fabio Estevam, Philipp Zabel, Mauro Carvalho Chehab, Hans Verkuil,
devicetree, imx, linux-arm-kernel, linux-kernel, linux-media,
stable
In-Reply-To: <20260424-csi2_imx8ulp-v12-1-da148eabc035@oss.nxp.com>
[-- Attachment #1: Type: text/plain, Size: 465 bytes --]
On Fri, Apr 24, 2026 at 02:49:50PM +0800, Guoniu Zhou wrote:
> The i.MX8ULP variant does not require the fsl,blk-ctrl property. Add
> fsl,imx8ulp-isi to the exception list alongside fsl,imx91-isi.
>
> Fixes: 288517a3c6c9 ("dt-bindings: media: nxp,imx8-isi: Add i.MX8ULP ISI compatible string")
> Cc: stable@vger.kernel.org
> Signed-off-by: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v1 1/3] dt-bindings: PCI: imx6q-pcie: Add intr, aer and pme interrupts
From: Conor Dooley @ 2026-04-24 17:06 UTC (permalink / raw)
To: Richard Zhu
Cc: robh, krzk+dt, conor+dt, bhelgaas, frank.li, l.stach, lpieralisi,
kwilczynski, mani, s.hauer, kernel, festevam, linux-pci,
linux-arm-kernel, devicetree, imx, linux-kernel
In-Reply-To: <20260424025735.1490772-2-hongxing.zhu@nxp.com>
[-- Attachment #1: Type: text/plain, Size: 1514 bytes --]
On Fri, Apr 24, 2026 at 10:57:33AM +0800, Richard Zhu wrote:
> Add optional 'intr', 'aer', and 'pme' interrupt entries to the i.MX6Q
> PCIe binding to support PCIe event-based interrupts for general
> controller events, Advanced Error Reporting, and Power Management Events
> respectively.
>
> Signed-off-by: Richard Zhu <hongxing.zhu@nxp.com>
> ---
This binding supports lots of devices. Do they all have these additional
interrupts?
> Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml
> index 9d1349855b422..badc7fcbd556c 100644
> --- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml
> +++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml
> @@ -58,12 +58,18 @@ properties:
> items:
> - description: builtin MSI controller.
> - description: builtin DMA controller.
> + - description: PCIe event interrupt.
> + - description: builtin AER SPI standalone interrupter line.
> + - description: builtin PME SPI standalone interrupter line.
>
> interrupt-names:
> minItems: 1
> items:
> - const: msi
> - const: dma
> + - const: intr
> + - const: aer
> + - const: pme
>
> reset-gpio:
> description: Should specify the GPIO for controlling the PCI bus device
> --
> 2.37.1
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v2] kselftest/arm64: Fix build failure with GCC-15
From: Mark Brown @ 2026-04-24 17:09 UTC (permalink / raw)
To: Catalin Marinas
Cc: Leo Yan, Will Deacon, Shuah Khan, Thiago Jung Bauermann,
linux-arm-kernel, linux-kselftest, linux-kernel
In-Reply-To: <aeud4Hqxq4PNitMc@arm.com>
[-- Attachment #1: Type: text/plain, Size: 624 bytes --]
On Fri, Apr 24, 2026 at 05:44:16PM +0100, Catalin Marinas wrote:
> OK, so it does look like it picks the kernel uapi/asm/ptrace.h. It
> builds fine on Debian stable (no GCS anywhere) with including
> asm/ptrace.h and removing struct user_gcs.
> But I think we should include asm/ptrace.h in libc-gcs.h and not the
> gcs-util.h header (for NT_ARM_GCS it's fine to keep in gcs-util.h).
It's not glibc specific so it seems reasonable to keep it in gcs-util.h,
even though we don't currently have any nolibc stuff that uses ptrace.
That said this isn't particularly strongly held opinion so I don't
*really* mind either way.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH] clk: bcm: rpi: Mark VEC clock as CLK_IGNORE_UNUSED
From: Brian Masney @ 2026-04-24 17:45 UTC (permalink / raw)
To: Mark Brown
Cc: Linus Torvalds, Michael Turquette, Stephen Boyd, Florian Fainelli,
Broadcom internal kernel review list, Maxime Ripard,
Maíra Canal, linux-clk, linux-rpi-kernel, linux-arm-kernel,
linux-kernel
In-Reply-To: <20260424-clk-bcm-fix-pi3-v1-1-8a734e94cecb@kernel.org>
On Fri, Apr 24, 2026 at 11:37 AM Mark Brown <broonie@kernel.org> wrote:
> From: Maíra Canal <mcanal@igalia.com>
>
> On Raspberry Pi 3B, the VEC clock is used by the VideoCore firmware
> display driver, which remains active until the vc4 driver loads and
> sends NOTIFY_DISPLAY_DONE. If this clock is disabled during boot, a bus
> lockup happens and the firmware becomes unresponsive, causing a complete
> system lockup.
>
> Mark the VEC clock with CLK_IGNORE_UNUSED so it survives the unused
> clock disablement and remains available until the vc4 driver takes over
> display management.
>
> Fixes: 672299736af6 ("clk: bcm: rpi: Manage clock rate in prepare/unprepare callbacks")
> Reported-by: Mark Brown <broonie@kernel.org>
> Closes: https://lore.kernel.org/r/5f0bec08-f458-4fba-8bf3-06817a100c4c@sirena.org.uk
> Signed-off-by: Maíra Canal <mcanal@igalia.com>
> Link: https://patch.msgid.link/20260401111416.562279-2-mcanal@igalia.com
> Tested-by: Mark Brown <broonie@kernel.org>
> Signed-off-by: Mark Brown <broonie@kernel.org>
Acked-by: Brian Masney <bmasney@redhat.com> # Active contributor to clk
I agree that this would be great to go in direct if possible.
^ permalink raw reply
* Re: [PATCH 1/2] dt-bindings: remoteproc: xlnx: add auto boot feature
From: Shah, Tanmay @ 2026-04-24 17:52 UTC (permalink / raw)
To: Krzysztof Kozlowski, tanmay.shah
Cc: andersson, mathieu.poirier, robh, krzk+dt, conor+dt, michal.simek,
ben.levinsky, linux-remoteproc, devicetree, linux-arm-kernel,
linux-kernel
In-Reply-To: <67f442f7-377d-46f3-82bc-86053e34c277@kernel.org>
On 4/24/2026 11:53 AM, Krzysztof Kozlowski wrote:
> On 23/04/2026 19:59, Shah, Tanmay wrote:
>> Ack, I will rename it to xlnx,auto-boot.
>>
>>>>
>>>>>> + type: boolean
>>>>>> + description: remote core is either already running or ready to boot
>>>>>
>>>>> And why is this property of a board?
>>>>>
>>>>
>>>> Not sure what indicates it is? The property is under remoteproc child
>>>> device that is SOC level property. Remote core is on same SOC wher linux
>>>> core is running.
>>>
>>> So it is implied by SoC compatible? Please provide some arguments why it
>>> cannot be implied by the SoC compatible. I gave you one way out, but if
>>> you disagree then no problem.
>>>
>>
>> So on some SoC, the bootloader supports loading and starting of the
>> remote processor. But it is totally user's choice. User can choose to
>> load & start one core of a cluster via bootloader and leave another core
>> powered-off.
>> That is why it is not possible to decide based on SoC compatible.
>
> OK. The problem is that "user" is a bit vague and usually user choice
> goes to user-space.
>
> The property will be set or unset for ALL of given boards. So all of the
> DTS->DTB. That's why it should be clear why all such boards should
> behave like you described. If this is truly user, as in user-space,
> choice, then DT is not the way.
>
Okay 'user' may not be the right choice of word. I should say 'hardware
configuration'. On same SoC, some cores can be configured to boot
automatically before Linux boots, and some won't. So if device-tree is
about hardware configuration, then we need a way to show which core is
configured to boot before linux. This configuration is board agnostic.
So I think auto-boot in device-tree makes sense.
The only advantage on this platform is, it has a way to detect if the
core is running or not runtime and don't have to rely on device-tree.
>
>>
>> If we don't want to make it a device-tree property then I can implement
>> in a different way. New way will detect if the remote is running or not
>> via EMMI/SCMI call to the firmware, and take a decision based on that.
>> If this new way works, then I don't think we need auto-boot property at all.
>>
>> Let me know your thoughts.
>
> This works for me and solves my questions from DT point of view, but I
> cannot judge whether this makes sense for you.
>
I say I will keep it open ended for now. I will avoid introducing
auto-boot in the device-tree for now, and send a patch without it. In
future if for some other reason this property is needed, will send new
patch later.
Thanks,
Tanmay
>
> Best regards,
> Krzysztof
^ permalink raw reply
* [RFC PATCH 0/5] media: rockchip: rkisp2: Add driver for ISP on Rk3588
From: Paul Elder @ 2026-04-24 17:58 UTC (permalink / raw)
To: laurent.pinchart
Cc: Paul Elder, michael.riesch, xuhf, stefan.klug, linux-media,
linux-arm-kernel, linux-rockchip, linux-kernel
Hello everyone!
This patch series adds a new rkisp2 driver that aims to support all
Rockchip 2.x and 3.x series ISPs. Here we add support for the version on
the RK3588, which as far as I'm aware is a 3.0.
Patches 1 and 2 add dt bindings and dt nodes; patches 3~5 add the actual
driver.
All my discussion points/questions/progress-reports are in patch 3.
The following two series [0] [1] will be helpful for testing the code,
as they enable the VICAP module to work on the RK3588. I've tested this
on the Orange Pi CM5 Base with an imx219. I have a branch here [2] for
convenience. A libcamera branch will be coming soon too!
[0] https://lore.kernel.org/all/20260216-rkcif-fixes-v2-0-ee40931fe0ff@collabora.com/
[1] https://lore.kernel.org/all/20250430-rk3588-vicap-v3-0-e38e428868cc@collabora.com/
[2] https://git.ideasonboard.com/epaul/linux/src/branch/epaul/v7.0/rk3588/rkisp2/upstream
Paul Elder (3):
media: dt-bindings: Add rockchip rkisp2 bindings
media: rkisp2: Add parameters output video node
media: rkisp2: Add statistics capture video node
Xu Hongfei (2):
arm64: dts: rockchip: add ISP nodes to rk3588
media: rockchip: rkisp2: Add rkisp2 driver
.../bindings/media/rockchip-isp2.yaml | 127 +
arch/arm64/boot/dts/rockchip/rk3588-base.dtsi | 60 +
drivers/media/platform/rockchip/Kconfig | 1 +
drivers/media/platform/rockchip/Makefile | 1 +
.../media/platform/rockchip/rkisp2/Kconfig | 19 +
.../media/platform/rockchip/rkisp2/Makefile | 13 +
.../platform/rockchip/rkisp2/rkisp2-capture.c | 1979 ++++++++++
.../platform/rockchip/rkisp2/rkisp2-common.c | 180 +
.../platform/rockchip/rkisp2/rkisp2-common.h | 565 +++
.../platform/rockchip/rkisp2/rkisp2-debug.c | 236 ++
.../platform/rockchip/rkisp2/rkisp2-dev.c | 376 ++
.../platform/rockchip/rkisp2/rkisp2-dmarx.c | 687 ++++
.../platform/rockchip/rkisp2/rkisp2-isp.c | 917 +++++
.../platform/rockchip/rkisp2/rkisp2-params.c | 1013 +++++
.../rockchip/rkisp2/rkisp2-regs-v2x.h | 2625 +++++++++++++
.../rockchip/rkisp2/rkisp2-regs-v3x.h | 3427 +++++++++++++++++
.../platform/rockchip/rkisp2/rkisp2-regs.h | 1736 +++++++++
.../platform/rockchip/rkisp2/rkisp2-stats.c | 482 +++
drivers/media/v4l2-core/v4l2-ioctl.c | 2 +
include/uapi/linux/rkisp1-config.h | 1 +
include/uapi/linux/rkisp2-config.h | 565 +++
include/uapi/linux/videodev2.h | 4 +
22 files changed, 15016 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/rockchip-isp2.yaml
create mode 100644 drivers/media/platform/rockchip/rkisp2/Kconfig
create mode 100644 drivers/media/platform/rockchip/rkisp2/Makefile
create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-capture.c
create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-common.c
create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-common.h
create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-debug.c
create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-dev.c
create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-dmarx.c
create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-isp.c
create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-params.c
create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v2x.h
create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v3x.h
create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-regs.h
create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-stats.c
create mode 100644 include/uapi/linux/rkisp2-config.h
--
2.47.2
^ permalink raw reply
* [RFC PATCH 1/5] media: dt-bindings: Add rockchip rkisp2 bindings
From: Paul Elder @ 2026-04-24 17:58 UTC (permalink / raw)
To: laurent.pinchart
Cc: Paul Elder, michael.riesch, xuhf, stefan.klug, linux-media,
linux-arm-kernel, linux-rockchip, linux-kernel, Heiko Stuebner
In-Reply-To: <20260424175853.638202-1-paul.elder@ideasonboard.com>
Add documentation for the Rockchip rkisp2 bindings. This is meant to
support multiple versions of Rockchip ISPs going forward, including the
2.x series and 3.x series. The current version only adds the compatible
for the RK3588, which is a 3.0.
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
---
.../bindings/media/rockchip-isp2.yaml | 127 ++++++++++++++++++
1 file changed, 127 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/rockchip-isp2.yaml
diff --git a/Documentation/devicetree/bindings/media/rockchip-isp2.yaml b/Documentation/devicetree/bindings/media/rockchip-isp2.yaml
new file mode 100644
index 000000000000..f4ef089adf6e
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/rockchip-isp2.yaml
@@ -0,0 +1,127 @@
+# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/rockchip-isp2.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip SoC Image Signal Processing unit v2
+
+maintainers:
+ - Paul Elder <paul.elder@ideasonboard.com>
+
+description: |
+ Rockchip ISP2 is the Camera interface for the Rockchip series of SoCs which
+ contains image processing, scaling, and compression functions.
+
+properties:
+ compatible:
+ enum:
+ - rockchip,rk3588-isp
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ minItems: 3
+ maxItems: 3
+
+ interrupt-names:
+ items:
+ - const: isp_irq
+ - const: mi_irq
+
+ clocks:
+ minItems: 3
+ items:
+ - description: ISP AXI clock (aclk)
+ - description: ISP AHB clock (hclk)
+ - description: ISP core clock (isp)
+ # for rk3588
+ - description: ISP core clock (marvin)
+ - description: ISP core clock (vicap)
+
+ clock-names:
+ minItems: 3
+ items:
+ - const: aclk
+ - const: hclk
+ - const: clk_core
+ # for rk3588
+ - const: clk_core_marvin
+ - const: clk_core_vicap
+
+ iommus:
+ maxItems: 1
+
+ power-domains:
+ minItems: 1
+ items:
+ - description: ISP power domain
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description: connection point for VICAP in inline mode
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ required:
+ - port@0
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+ - power-domains
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: rockchip,rk3588-isp
+ then:
+ properties:
+ clocks:
+ minItems: 5
+ clock-names:
+ minItems: 5
+
+additionalProperties: false
+
+examples:
+ - |
+
+ #include <dt-bindings/clock/rockchip,rk3588-cru.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/power/rk3588-power.h>
+
+ parent0: parent {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ isp0: isp@fdcb0000 {
+ compatible = "rockchip,rk3588-isp";
+ reg = <0x0 0xfdcb0000 0x0 0x7f00>;
+ interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 134 IRQ_TYPE_LEVEL_HIGH 0>;
+ interrupt-names = "isp_irq", "mi_irq";
+ clocks = <&cru ACLK_ISP0>, <&cru HCLK_ISP0>,
+ <&cru CLK_ISP0_CORE>, <&cru CLK_ISP0_CORE_MARVIN>,
+ <&cru CLK_ISP0_CORE_VICAP>;
+ clock-names = "aclk", "hclk", "clk_core",
+ "clk_core_marvin", "clk_core_vicap";
+ power-domains = <&power RK3588_PD_VI>;
+ iommus = <&isp0_mmu>;
+ };
+ };
--
2.47.2
^ permalink raw reply related
* [RFC PATCH 2/5] arm64: dts: rockchip: add ISP nodes to rk3588
From: Paul Elder @ 2026-04-24 17:58 UTC (permalink / raw)
To: laurent.pinchart
Cc: Xu Hongfei, michael.riesch, stefan.klug, linux-media,
linux-arm-kernel, linux-rockchip, linux-kernel, Paul Elder,
Heiko Stuebner
In-Reply-To: <20260424175853.638202-1-paul.elder@ideasonboard.com>
From: Xu Hongfei <xuhf@rock-chips.com>
Add device tree nodes for the ISP and their iommus on the RK3588.
Signed-off-by: Xu Hongfei <xuhf@rock-chips.com>
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
---
arch/arm64/boot/dts/rockchip/rk3588-base.dtsi | 60 +++++++++++++++++++
1 file changed, 60 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
index 8b98e5c3cc8b..607b03d55dfd 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
@@ -3535,6 +3535,66 @@ gpio4: gpio@fec50000 {
#interrupt-cells = <2>;
};
};
+
+ isp0: isp@fdcb0000 {
+ compatible = "rockchip,rk3588-isp";
+ reg = <0x0 0xfdcb0000 0x0 0x7f00>;
+ interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 134 IRQ_TYPE_LEVEL_HIGH 0>;
+ interrupt-names = "isp_irq", "mi_irq";
+ clocks = <&cru ACLK_ISP0>, <&cru HCLK_ISP0>,
+ <&cru CLK_ISP0_CORE>, <&cru CLK_ISP0_CORE_MARVIN>,
+ <&cru CLK_ISP0_CORE_VICAP>;
+ clock-names = "aclk", "hclk", "clk_core",
+ "clk_core_marvin", "clk_core_vicap";
+ power-domains = <&power RK3588_PD_VI>;
+ iommus = <&isp0_mmu>;
+ status = "disabled";
+ };
+
+ isp0_mmu: iommu@fdcb7f00 {
+ compatible = "rockchip,rk3588-iommu", "rockchip,rk3568-iommu";
+ reg = <0x0 0xfdcb7f00 0x0 0x100>;
+ interrupts = <GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH 0>;
+ interrupt-names = "isp0_mmu";
+ clocks = <&cru ACLK_ISP0>, <&cru HCLK_ISP0>;
+ clock-names = "aclk", "iface";
+ power-domains = <&power RK3588_PD_VI>;
+ #iommu-cells = <0>;
+ rockchip,disable-mmu-reset;
+ status = "disabled";
+ };
+
+ isp1: isp@fdcc0000 {
+ compatible = "rockchip,rk3588-isp";
+ reg = <0x0 0xfdcc0000 0x0 0x7f00>;
+ interrupts = <GIC_SPI 135 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH 0>;
+ interrupt-names = "isp_irq", "mi_irq";
+ clocks = <&cru ACLK_ISP1>, <&cru HCLK_ISP1>,
+ <&cru CLK_ISP1_CORE>, <&cru CLK_ISP1_CORE_MARVIN>,
+ <&cru CLK_ISP1_CORE_VICAP>;
+ clock-names = "aclk", "hclk", "clk_core",
+ "clk_core_marvin", "clk_core_vicap";
+ power-domains = <&power RK3588_PD_ISP1>;
+ iommus = <&isp1_mmu>;
+ status = "disabled";
+ };
+
+ isp1_mmu: iommu@fdcc7f00 {
+ compatible = "rockchip,rk3588-iommu", "rockchip,rk3568-iommu";
+ reg = <0x0 0xfdcc7f00 0x0 0x100>;
+ interrupts = <GIC_SPI 136 IRQ_TYPE_LEVEL_HIGH 0>;
+ interrupt-names = "isp1_mmu";
+ clocks = <&cru ACLK_ISP1>, <&cru HCLK_ISP1>;
+ clock-names = "aclk", "iface";
+ power-domains = <&power RK3588_PD_ISP1>;
+ #iommu-cells = <0>;
+ rockchip,disable-mmu-reset;
+ status = "disabled";
+ };
};
#include "rk3588-base-pinctrl.dtsi"
--
2.47.2
^ permalink raw reply related
* [RFC PATCH 4/5] media: rkisp2: Add parameters output video node
From: Paul Elder @ 2026-04-24 17:58 UTC (permalink / raw)
To: laurent.pinchart
Cc: Paul Elder, michael.riesch, xuhf, stefan.klug, linux-media,
linux-arm-kernel, linux-rockchip, linux-kernel
In-Reply-To: <20260424175853.638202-1-paul.elder@ideasonboard.com>
Implement support for setting parameters on the ISP by queueing
parameter buffers to rkisp2.
Support for the following parameters is added:
- BLS (black level subtraction)
- AWB gains (color gains)
- CSM (color space conversion)
- CCM (color correction matrix)
- GOC (gamma out correction)
- LSC (lens shading correction)
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
---
.../media/platform/rockchip/rkisp2/Makefile | 3 +-
.../platform/rockchip/rkisp2/rkisp2-common.h | 50 ++
.../platform/rockchip/rkisp2/rkisp2-dev.c | 15 +
.../platform/rockchip/rkisp2/rkisp2-isp.c | 11 +-
.../platform/rockchip/rkisp2/rkisp2-params.c | 775 ++++++++++++++++++
.../rockchip/rkisp2/rkisp2-regs-v3x.h | 31 +
drivers/media/v4l2-core/v4l2-ioctl.c | 1 +
include/uapi/linux/rkisp2-config.h | 314 +++++++
include/uapi/linux/videodev2.h | 3 +
9 files changed, 1201 insertions(+), 2 deletions(-)
create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-params.c
diff --git a/drivers/media/platform/rockchip/rkisp2/Makefile b/drivers/media/platform/rockchip/rkisp2/Makefile
index 9a9d7b5233c4..0fa014afcae4 100644
--- a/drivers/media/platform/rockchip/rkisp2/Makefile
+++ b/drivers/media/platform/rockchip/rkisp2/Makefile
@@ -4,7 +4,8 @@ rockchip-isp2-y := rkisp2-capture.o \
rkisp2-common.o \
rkisp2-dev.o \
rkisp2-dmarx.o \
- rkisp2-isp.o
+ rkisp2-isp.o \
+ rkisp2-params.o
rockchip-isp2-$(CONFIG_DEBUG_FS) += rkisp2-debug.o
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-common.h b/drivers/media/platform/rockchip/rkisp2/rkisp2-common.h
index e08adfec2c50..7473dae6c525 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-common.h
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-common.h
@@ -22,6 +22,7 @@
#include <media/media-entity.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-isp.h>
#include <media/videobuf2-v4l2.h>
#include "rkisp2-regs.h"
@@ -88,6 +89,7 @@ enum rkisp2_fmt_raw_pat_type {
/* enum for the isp pads */
enum rkisp2_isp_pad {
RKISP2_ISP_PAD_SINK_VIDEO,
+ RKISP2_ISP_PAD_SINK_PARAMS,
RKISP2_ISP_PAD_SOURCE_VIDEO,
RKISP2_ISP_PAD_MAX
};
@@ -182,6 +184,26 @@ struct rkisp2_buffer {
dma_addr_t buff_addr[VIDEO_MAX_PLANES];
};
+/*
+ * struct rkisp2_params_buffer - A container for the vb2 buffers used by the
+ * params video device
+ *
+ * @vb: vb2 buffer
+ * @queue: entry of the buffer in the queue
+ * @cfg: scratch buffer used for caching the ISP configuration parameters
+ */
+struct rkisp2_params_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head queue;
+ struct v4l2_isp_params_buffer *cfg;
+};
+
+static inline struct rkisp2_params_buffer *
+to_rkisp2_params_buffer(struct vb2_v4l2_buffer *vbuf)
+{
+ return container_of(vbuf, struct rkisp2_params_buffer, vb);
+}
+
/*
* struct rkisp2_dummy_buffer - A buffer to write the next frame to in case
* there are no vb2 buffers available.
@@ -292,6 +314,25 @@ struct rkisp2_capture {
struct v4l2_rect crop;
};
+/*
+ * struct rkisp2_params - ISP input parameters device
+ *
+ * @vnode: video node
+ * @rkisp2: pointer to the rkisp2 device
+ * @buf_lock: locks the buffer list 'params'
+ * @params: queue of rkisp2_buffer
+ * @raw_type: the bayer pattern on the isp video sink pad
+ */
+struct rkisp2_params {
+ struct rkisp2_vdev_node vnode;
+ struct rkisp2_device *rkisp2;
+
+ spinlock_t buf_lock; /* locks the buffers list 'params' */
+ struct list_head params;
+
+ enum rkisp2_fmt_raw_pat_type raw_type;
+};
+
struct rkisp2_debug {
struct dentry *debugfs_dir;
unsigned long data_loss;
@@ -324,6 +365,7 @@ struct rkisp2_debug {
* @resizer_devs: resizer sub-devices
* @capture_devs: capture devices
* @dmarx: ISP memory read device
+ * @params: ISP parameters metadata output device
* @pipe: media pipeline
* @stream_lock: serializes {start/stop}_streaming callbacks between the capture devices.
* @debug: debug params to be exposed on debugfs
@@ -345,6 +387,7 @@ struct rkisp2_device {
struct rkisp2_isp isp;
struct rkisp2_capture capture_devs[2];
struct rkisp2_dmarx dmarx;
+ struct rkisp2_params params;
struct media_pipeline pipe;
struct mutex stream_lock; /* serialize {start/stop}_streaming cb between capture devices */
struct rkisp2_debug debug;
@@ -434,6 +477,7 @@ irqreturn_t rkisp2_isp_isr(int irq, void *ctx);
irqreturn_t rkisp2_capture_isr(int irq, void *ctx);
irqreturn_t rkisp2_mipi_isr(int irq, void *ctx);
void rkisp2_dmarx_isr(struct rkisp2_device *rkisp2, u32 status);
+void rkisp2_params_isr(struct rkisp2_params *params);
/* register/unregisters functions of the entities */
int rkisp2_capture_devs_register(struct rkisp2_device *rkisp2);
@@ -444,6 +488,12 @@ void rkisp2_isp_unregister(struct rkisp2_device *rkisp2);
int rkisp2_dmarx_register(struct rkisp2_device *rkisp2);
void rkisp2_dmarx_unregister(struct rkisp2_device *rkisp2);
+int rkisp2_params_register(struct rkisp2_device *rkisp2);
+void rkisp2_params_unregister(struct rkisp2_device *rkisp2);
+void rkisp2_params_pre_configure(struct rkisp2_params *params,
+ enum rkisp2_fmt_raw_pat_type bayer_pat);
+void rkisp2_params_post_configure(struct rkisp2_params *params);
+
#if IS_ENABLED(CONFIG_DEBUG_FS)
void rkisp2_debug_init(struct rkisp2_device *rkisp2);
void rkisp2_debug_cleanup(struct rkisp2_device *rkisp2);
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-dev.c b/drivers/media/platform/rockchip/rkisp2/rkisp2-dev.c
index 4d5c41850395..0356ef2a1cf1 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-dev.c
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-dev.c
@@ -129,11 +129,22 @@ static int rkisp2_create_links(struct rkisp2_device *rkisp2)
return ret;
}
+ /* params links */
+ ret = media_create_pad_link(&rkisp2->params.vnode.vdev.entity, 0,
+ &rkisp2->isp.sd.entity,
+ RKISP2_ISP_PAD_SINK_PARAMS,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret)
+ return ret;
+
+
return 0;
}
static void rkisp2_entities_unregister(struct rkisp2_device *rkisp2)
{
+ rkisp2_params_unregister(rkisp2);
rkisp2_dmarx_unregister(rkisp2);
rkisp2_capture_devs_unregister(rkisp2);
rkisp2_isp_unregister(rkisp2);
@@ -155,6 +166,10 @@ static int rkisp2_entities_register(struct rkisp2_device *rkisp2)
if (ret)
goto error;
+ ret = rkisp2_params_register(rkisp2);
+ if (ret)
+ goto error;
+
ret = rkisp2_create_links(rkisp2);
if (ret)
goto error;
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-isp.c b/drivers/media/platform/rockchip/rkisp2/rkisp2-isp.c
index 4140139da3c1..0967d5772bc9 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-isp.c
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-isp.c
@@ -120,6 +120,10 @@ static int rkisp2_config_isp(struct rkisp2_isp *isp,
RKISP2_CIF_ISP_PIC_SIZE_ERROR;
rkisp2_write(rkisp2, RKISP2_CIF_ISP_IMSC, irq_mask);
+ src_frm = v4l2_subdev_state_get_format(sd_state,
+ RKISP2_ISP_PAD_SOURCE_VIDEO);
+ rkisp2_params_pre_configure(&rkisp2->params, sink_fmt->bayer_pat);
+
isp->sink_fmt = sink_fmt;
return 0;
@@ -248,6 +252,8 @@ static int rkisp2_isp_start(struct rkisp2_isp *isp,
RKISP2_CIF_ISP_CTRL_ISP_INFORM_ENABLE | RKISP2_CIF_ISP_CTRL_ISP_CFG_UPD_PERMANENT;
rkisp2_write(rkisp2, RKISP2_CIF_ISP_CTRL, val);
+ rkisp2_params_post_configure(&rkisp2->params);
+
return 0;
}
@@ -804,6 +810,7 @@ int rkisp2_isp_register(struct rkisp2_device *rkisp2)
pads[RKISP2_ISP_PAD_SINK_VIDEO].flags = MEDIA_PAD_FL_SINK |
MEDIA_PAD_FL_MUST_CONNECT;
+ pads[RKISP2_ISP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK;
pads[RKISP2_ISP_PAD_SOURCE_VIDEO].flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_pads_init(&sd->entity, RKISP2_ISP_PAD_MAX, pads);
@@ -893,8 +900,10 @@ irqreturn_t rkisp2_isp_isr(int irq, void *ctx)
rkisp2->debug.data_loss++;
}
- if (status & RKISP2_CIF_ISP_FRAME)
+ if (status & RKISP2_CIF_ISP_FRAME) {
rkisp2->debug.complete_frames++;
+ rkisp2_params_isr(&rkisp2->params);
+ }
rkisp2_write(rkisp2, RKISP2_CIF_ISP_ICR, status);
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-params.c b/drivers/media/platform/rockchip/rkisp2/rkisp2-params.c
new file mode 100644
index 000000000000..b7b27d0e90c6
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-params.c
@@ -0,0 +1,775 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR MIT)
+/*
+ * Rockchip ISP2 Driver - Params subdevice
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2026 Ideas on Board Oy.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/build_bug.h>
+#include <linux/math.h>
+#include <linux/string.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-isp.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include "rkisp2-common.h"
+
+#define RKISP2_PARAMS_DEV_NAME RKISP2_DRIVER_NAME "_params"
+
+#define RKISP2_PARAMS_BLOCK_INFO(block, data) \
+ [RKISP2_PARAMS_BLOCK_ ## block] = { \
+ .size = sizeof(struct rkisp2_params_ ## data ), \
+ }
+
+#define RKISP2_PARAMS_BLOCK_HANDLER_INFO(block, handler_postfix, prio_postfix) \
+ [RKISP2_PARAMS_BLOCK_ ## block] = { \
+ .handler = rkisp2_params_ ## handler_postfix,\
+ .priority = RKISP2_PARAMS_CONFIG_PRIO_ ## prio_postfix, \
+ }
+
+union rkisp2_params_block {
+ const struct v4l2_isp_params_block_header *header;
+ const struct rkisp2_params_bls *bls;
+ const struct rkisp2_params_awb_gains *awb_gains;
+ const struct rkisp2_params_csm *csm;
+ const struct rkisp2_params_ccm *ccm;
+ const struct rkisp2_params_goc *goc;
+ const struct rkisp2_params_lsc *lsc;
+ const __u8 *data;
+};
+
+static void rkisp2_params_bls(struct rkisp2_params *params,
+ union rkisp2_params_block block);
+static void rkisp2_params_awb_gains(struct rkisp2_params *params,
+ union rkisp2_params_block block);
+static void rkisp2_params_csm(struct rkisp2_params *params,
+ union rkisp2_params_block block);
+static void rkisp2_params_ccm(struct rkisp2_params *params,
+ union rkisp2_params_block block);
+static void rkisp2_params_goc(struct rkisp2_params *params,
+ union rkisp2_params_block block);
+static void rkisp2_params_lsc(struct rkisp2_params *params,
+ union rkisp2_params_block block);
+
+typedef void (*rkisp2_params_handler)(struct rkisp2_params *params,
+ const union rkisp2_params_block block);
+
+enum rkisp2_params_configure_priority {
+ RKISP2_PARAMS_CONFIG_PRIO_NONE = 0,
+ RKISP2_PARAMS_CONFIG_PRIO_PRE,
+ RKISP2_PARAMS_CONFIG_PRIO_POST,
+};
+
+struct rkisp2_params_block_handler_info {
+ rkisp2_params_handler handler;
+ enum rkisp2_params_configure_priority priority;
+};
+
+static const struct rkisp2_params_block_handler_info
+rkisp2_params_handlers[] = {
+ RKISP2_PARAMS_BLOCK_HANDLER_INFO(BLS, bls, PRE),
+ RKISP2_PARAMS_BLOCK_HANDLER_INFO(AWB_GAINS, awb_gains, PRE),
+ RKISP2_PARAMS_BLOCK_HANDLER_INFO(CSM, csm, PRE),
+ RKISP2_PARAMS_BLOCK_HANDLER_INFO(CCM, ccm, PRE),
+ RKISP2_PARAMS_BLOCK_HANDLER_INFO(GOC, goc, PRE),
+ RKISP2_PARAMS_BLOCK_HANDLER_INFO(LSC, lsc, POST),
+};
+
+static const struct v4l2_isp_params_block_type_info
+rkisp2_params_block_types_info[] = {
+ RKISP2_PARAMS_BLOCK_INFO(BLS, bls),
+ RKISP2_PARAMS_BLOCK_INFO(AWB_GAINS, awb_gains),
+ RKISP2_PARAMS_BLOCK_INFO(CSM, csm),
+ RKISP2_PARAMS_BLOCK_INFO(CCM, ccm),
+ RKISP2_PARAMS_BLOCK_INFO(GOC, goc),
+ RKISP2_PARAMS_BLOCK_INFO(LSC, lsc),
+};
+
+static_assert(ARRAY_SIZE(rkisp2_params_handlers) ==
+ ARRAY_SIZE(rkisp2_params_block_types_info));
+
+static inline void
+rkisp2_param_set_bits(struct rkisp2_params *params, u32 reg, u32 bit_mask)
+{
+ u32 val;
+
+ val = rkisp2_read(params->rkisp2, reg);
+ rkisp2_write(params->rkisp2, reg, val | bit_mask);
+}
+
+static inline void
+rkisp2_param_clear_bits(struct rkisp2_params *params, u32 reg, u32 bit_mask)
+{
+ u32 val;
+
+ val = rkisp2_read(params->rkisp2, reg);
+ rkisp2_write(params->rkisp2, reg, val & ~bit_mask);
+}
+
+static void rkisp2_params_bls(struct rkisp2_params *params,
+ union rkisp2_params_block block)
+{
+ const struct rkisp2_params_bls *arg = block.bls;
+ u32 control;
+
+ if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp2_param_clear_bits(params, ISP_BLS_CTRL, ISP_BLS_ENA | ISP_BLS_BLS1_EN);
+ return;
+ }
+
+ if (!(block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+ return;
+
+ control = ISP_BLS_ENA;
+
+ if (!arg->enable_auto) {
+ /* TODO plumb BLS1 */
+
+ rkisp2_write(params->rkisp2, ISP_BLS_A_FIXED, arg->bls_fixed_val.a);
+ rkisp2_write(params->rkisp2, ISP_BLS_B_FIXED, arg->bls_fixed_val.b);
+ rkisp2_write(params->rkisp2, ISP_BLS_C_FIXED, arg->bls_fixed_val.c);
+ rkisp2_write(params->rkisp2, ISP_BLS_D_FIXED, arg->bls_fixed_val.d);
+
+ /* Set fixed mode */
+ rkisp2_param_clear_bits(params, ISP_BLS_CTRL,
+ ISP_BLS_MODE_MEASURED);
+ return;
+ }
+
+ if (arg->enabled_windows & BIT(1)) {
+ rkisp2_write(params->rkisp2, ISP_BLS_H2_START,
+ arg->bls_window2.h_offs);
+ rkisp2_write(params->rkisp2, ISP_BLS_H2_STOP,
+ arg->bls_window2.h_size);
+ rkisp2_write(params->rkisp2, ISP_BLS_V2_START,
+ arg->bls_window2.v_offs);
+ rkisp2_write(params->rkisp2, ISP_BLS_V2_STOP,
+ arg->bls_window2.v_size);
+ control |= ISP_BLS_WINDOW_2;
+ }
+
+ if (arg->enabled_windows & BIT(0)) {
+ rkisp2_write(params->rkisp2, ISP_BLS_H1_START,
+ arg->bls_window1.h_offs);
+ rkisp2_write(params->rkisp2, ISP_BLS_H1_STOP,
+ arg->bls_window1.h_size);
+ rkisp2_write(params->rkisp2, ISP_BLS_V1_START,
+ arg->bls_window1.v_offs);
+ rkisp2_write(params->rkisp2, ISP_BLS_V1_STOP,
+ arg->bls_window1.v_size);
+ control |= ISP_BLS_WINDOW_1;
+ }
+
+ rkisp2_write(params->rkisp2, ISP_BLS_SAMPLES,
+ arg->bls_samples);
+
+ control |= ISP_BLS_MODE_MEASURED;
+ rkisp2_write(params->rkisp2, ISP_BLS_CTRL, control);
+}
+
+static void rkisp2_params_awb_gains(struct rkisp2_params *params,
+ union rkisp2_params_block block)
+{
+ const struct rkisp2_params_awb_gains *arg = block.awb_gains;
+ unsigned int i;
+
+ if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp2_param_clear_bits(params, RKISP2_CIF_ISP_CTRL,
+ RKISP2_CIF_ISP_CTRL_ISP_AWB_ENA);
+ return;
+ }
+
+ if (!(block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(arg->gains); i++) {
+ rkisp2_write(params->rkisp2, ISP21_AWB_GAIN0_G + i * 8,
+ arg->gains[i].gr << 16 | arg->gains[i].gb);
+ rkisp2_write(params->rkisp2, ISP21_AWB_GAIN0_RB + i * 8,
+ arg->gains[i].r << 16 | arg->gains[i].b);
+ }
+
+ rkisp2_param_set_bits(params, RKISP2_CIF_ISP_CTRL,
+ RKISP2_CIF_ISP_CTRL_ISP_AWB_ENA);
+}
+
+static void rkisp2_params_csm_reset(struct rkisp2_params *params)
+{
+ /* Write back the default values. */
+ rkisp2_write(params->rkisp2, RKISP2_CIF_ISP_CC_COEFF_0, 0x80);
+ rkisp2_write(params->rkisp2, RKISP2_CIF_ISP_CC_COEFF_1, 0);
+ rkisp2_write(params->rkisp2, RKISP2_CIF_ISP_CC_COEFF_2, 0);
+ rkisp2_write(params->rkisp2, RKISP2_CIF_ISP_CC_COEFF_3, 0);
+ rkisp2_write(params->rkisp2, RKISP2_CIF_ISP_CC_COEFF_4, 0x80);
+ rkisp2_write(params->rkisp2, RKISP2_CIF_ISP_CC_COEFF_5, 0);
+ rkisp2_write(params->rkisp2, RKISP2_CIF_ISP_CC_COEFF_6, 0);
+ rkisp2_write(params->rkisp2, RKISP2_CIF_ISP_CC_COEFF_7, 0);
+ rkisp2_write(params->rkisp2, RKISP2_CIF_ISP_CC_COEFF_8, 0x80);
+}
+
+static void rkisp2_params_csm(struct rkisp2_params *params,
+ union rkisp2_params_block block)
+{
+ const struct rkisp2_params_csm *arg = block.csm;
+ unsigned int i, j, k = 0;
+
+ if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp2_params_csm_reset(params);
+ return;
+ }
+
+ if (!(block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+ return;
+
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ rkisp2_write(params->rkisp2,
+ RKISP2_CIF_ISP_CC_COEFF_0 + 4 * k++,
+ arg->coeff[i][j]);
+}
+
+static void rkisp2_params_ccm(struct rkisp2_params *params,
+ union rkisp2_params_block block)
+{
+ const struct rkisp2_params_ccm *arg = block.ccm;
+ unsigned int i;
+ u32 control = 0;
+
+ if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp2_param_clear_bits(params, ISP_CCM_CTRL, ISP_CCM_EN);
+ return;
+ }
+
+ if (!(block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+ return;
+
+ for (i = 0; i < 3; i++) {
+ rkisp2_write(params->rkisp2, ISP_CCM_COEFF0_R + 8 * i,
+ ISP3X_CCM_COEFF(arg->coeff[i][0], arg->coeff[i][1]));
+ rkisp2_write(params->rkisp2, ISP_CCM_COEFF1_R + 8 * i,
+ ISP3X_CCM_COEFF(arg->coeff[i][2], arg->offset[i]));
+ }
+
+ rkisp2_write(params->rkisp2, ISP_CCM_COEFF0_Y,
+ ISP3X_CCM_COEFF(arg->y_coeff[0], arg->y_coeff[1]));
+ rkisp2_write(params->rkisp2, ISP_CCM_COEFF1_Y, arg->y_coeff[2]);
+
+ for (i = 0; i < 8; i++)
+ rkisp2_write(params->rkisp2, ISP_CCM_ALP_Y0 + 4 * i,
+ ISP3X_CCM_COEFF(arg->alp[2 * i], arg->alp[2 * i + 1]));
+ rkisp2_write(params->rkisp2, ISP_CCM_ALP_Y8, arg->alp[16]);
+
+ rkisp2_write(params->rkisp2, ISP_CCM_BOUND_BIT, arg->inflection_point);
+
+ if (!arg->high_y_alpha_adj_en)
+ control = ISP3X_CCM_HIGHY_ADJ_DIS;
+ control |= ISP_CCM_EN;
+ rkisp2_write(params->rkisp2, ISP_CCM_CTRL, control);
+}
+
+static void rkisp2_params_goc(struct rkisp2_params *params,
+ union rkisp2_params_block block)
+{
+ const struct rkisp2_params_goc *arg = block.goc;
+ unsigned int i;
+ u32 control = 0;
+
+ if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp2_param_clear_bits(params, ISP3X_GAMMA_OUT_CTRL,
+ ISP3X_GAMMA_OUT_CTRL_EN);
+ return;
+ }
+
+ if (!(block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+ return;
+
+ for (i = 0; i < RKISP2_ISP_GAMMA_OUT_MAX_SEGMENTS >> 1; i++)
+ rkisp2_write(params->rkisp2,
+ ISP3X_GAMMA_OUT_Y0 + 4 * i,
+ ISP3X_GAMMA_OUT_SAMPLE(arg->gamma_y[2 * i],
+ arg->gamma_y[2 * i + 1]));
+
+ rkisp2_write(params->rkisp2, ISP3X_GAMMA_OUT_Y24,
+ arg->gamma_y[RKISP2_ISP_GAMMA_OUT_MAX_SEGMENTS - 1]);
+
+ rkisp2_write(params->rkisp2, ISP3X_GAMMA_OUT_OFFSET, arg->offset);
+
+ if (arg->mode == RKISP2_ISP_GOC_MODE_EQUIDISTANT)
+ control = ISP3X_GAMMA_OUT_CTRL_MODE_EQUIDISTANT;
+ if (arg->mode == RKISP2_ISP_GOC_SEGMENTS_48)
+ control |= ISP3X_GAMMA_OUT_CTRL_SEGMENTS_48;
+ control |= ISP3X_GAMMA_OUT_CTRL_EN;
+ rkisp2_write(params->rkisp2, ISP3X_GAMMA_OUT_CTRL, control);
+}
+
+static void rkisp2_params_lsc(struct rkisp2_params *params,
+ union rkisp2_params_block block)
+{
+ const struct rkisp2_params_lsc *arg = block.lsc;
+ struct rkisp2_device *rkisp2 = params->rkisp2;
+ u32 sram_addr;
+ u32 data;
+ unsigned int i, j, table_i;
+
+ if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp2_param_clear_bits(params, ISP3X_LSC_CTRL, ISP3X_LSC_CTRL_EN);
+ return;
+ }
+
+ if (!(block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+ return;
+
+ if (arg->set_active_table_when == RKISP2_ISP_LSC_SET_ACTIVE_TABLE_BEFORE)
+ rkisp2_write(rkisp2, ISP3X_LSC_TABLE_SEL, arg->active_table ? 1 : 0);
+
+ /*
+ * - No need to disable the lsc before writing the table
+ * - Table 0 starts at 0, table 1 starts at 153.
+ * - TABLE_SEL selects which table is active, but programming the tables
+ * is done by just writing to the right address.
+ * - The address automatically increments, so no need to increment it.
+ */
+
+ for (table_i = 0; table_i < 2; table_i++) {
+ if (!arg->write_table[table_i])
+ continue;
+
+ sram_addr = table_i * 153;
+
+ rkisp2_write(rkisp2, ISP3X_LSC_R_TABLE_ADDR, sram_addr);
+ rkisp2_write(rkisp2, ISP3X_LSC_GR_TABLE_ADDR, sram_addr);
+ rkisp2_write(rkisp2, ISP3X_LSC_B_TABLE_ADDR, sram_addr);
+ rkisp2_write(rkisp2, ISP3X_LSC_GB_TABLE_ADDR, sram_addr);
+
+ /* Program data tables (table size is 9 * 17 = 153) */
+ for (i = 0; i < RKISP2_ISP_LSC_SAMPLES_MAX; i++) {
+ const __u16 *r_row = arg->r_data_tbl[table_i][i];
+ const __u16 *gr_row = arg->gr_data_tbl[table_i][i];
+ const __u16 *gb_row = arg->gb_data_tbl[table_i][i];
+ const __u16 *b_row = arg->b_data_tbl[table_i][i];
+
+ /*
+ * 17 sectors with 2 values in one DWORD = 9
+ * DWORDs (2nd value of last DWORD unused)
+ */
+ for (j = 0; j < RKISP2_ISP_LSC_SAMPLES_MAX / 2; j++) {
+ rkisp2_write(rkisp2, ISP3X_LSC_R_TABLE_DATA,
+ ISP3X_LSC_TABLE_DATA(r_row[2 * j], r_row[2 * j + 1]));
+ rkisp2_write(rkisp2, ISP3X_LSC_GR_TABLE_DATA,
+ ISP3X_LSC_TABLE_DATA(gr_row[2 * j], gr_row[2 * j + 1]));
+ rkisp2_write(rkisp2, ISP3X_LSC_GB_TABLE_DATA,
+ ISP3X_LSC_TABLE_DATA(gb_row[2 * j], gb_row[2 * j + 1]));
+ rkisp2_write(rkisp2, ISP3X_LSC_B_TABLE_DATA,
+ ISP3X_LSC_TABLE_DATA(b_row[2 * j], b_row[2 * j + 1]));
+ }
+
+ rkisp2_write(rkisp2, ISP3X_LSC_R_TABLE_DATA,
+ ISP3X_LSC_TABLE_DATA(r_row[2 * j], 0));
+ rkisp2_write(rkisp2, ISP3X_LSC_GR_TABLE_DATA,
+ ISP3X_LSC_TABLE_DATA(gr_row[2 * j], 0));
+ rkisp2_write(rkisp2, ISP3X_LSC_GB_TABLE_DATA,
+ ISP3X_LSC_TABLE_DATA(gb_row[2 * j], 0));
+ rkisp2_write(rkisp2, ISP3X_LSC_B_TABLE_DATA,
+ ISP3X_LSC_TABLE_DATA(b_row[2 * j], 0));
+
+ }
+ }
+
+ /* Program grid sizes and interpolation gradients */
+ for (i = 0; i < RKISP2_ISP_LSC_SECTORS_TBL_SIZE_MAX / 2; i++) {
+ /* program x size tables */
+ data = ISP3X_LSC_SECT_SIZE(arg->x_sizes[i * 2],
+ arg->x_sizes[i * 2 + 1]);
+ rkisp2_write(rkisp2, ISP3X_LSC_XSIZE(i), data);
+
+ /* program x grad tables */
+ data = ISP3X_LSC_GRAD_SIZE(arg->x_grads[i * 2],
+ arg->x_grads[i * 2 + 1]);
+ rkisp2_write(rkisp2, ISP3X_LSC_XGRAD(i), data);
+
+ /* program y size tables */
+ data = ISP3X_LSC_SECT_SIZE(arg->y_sizes[i * 2],
+ arg->y_sizes[i * 2 + 1]);
+ rkisp2_write(rkisp2, ISP3X_LSC_YSIZE(i), data);
+
+ /* program y grad tables */
+ data = ISP3X_LSC_GRAD_SIZE(arg->y_grads[i * 2],
+ arg->y_grads[i * 2 + 1]);
+ rkisp2_write(rkisp2, ISP3X_LSC_YGRAD(i), data);
+ }
+
+ rkisp2_write(rkisp2, ISP3X_LSC_TABLE_SEL, arg->active_table ? 1 : 0);
+
+ data = 0;
+ if (arg->window_mode)
+ data |= ISP3X_LSC_SECTOR_16X16;
+ /* TODO plumb the rest of the ctrl fields */
+ data |= ISP3X_LSC_CTRL_EN;
+
+ rkisp2_param_set_bits(params, ISP3X_LSC_CTRL, data);
+}
+
+static void rkisp2_params_configure(struct rkisp2_params *params,
+ struct rkisp2_params_buffer *buf,
+ enum rkisp2_params_configure_priority prio)
+{
+ struct v4l2_isp_params_buffer *cfg;
+ const struct rkisp2_params_block_handler_info *info;
+ size_t block_offset = 0;
+ size_t max_offset;
+
+ buf->vb.sequence = params->rkisp2->isp.frame_sequence + 1;
+ cfg = buf->cfg;
+
+ max_offset = cfg->data_size;
+
+ /* Walk the list of parameter blocks and process them. */
+ while (max_offset && block_offset < max_offset) {
+ union rkisp2_params_block block;
+
+ /* \todo Check if we want to avoid this copy */
+ block.data = &cfg->data[block_offset];
+
+ block_offset += block.header->size;
+
+ info = &rkisp2_params_handlers[block.header->type];
+
+ if (prio != RKISP2_PARAMS_CONFIG_PRIO_NONE &&
+ prio != info->priority)
+ continue;
+
+ info->handler(params, block);
+ }
+
+ /* update shadow register immediately */
+ rkisp2_param_set_bits(params, RKISP2_CIF_ISP_CTRL,
+ RKISP2_CIF_ISP_CTRL_ISP_CFG_UPD);
+
+ if (prio == RKISP2_PARAMS_CONFIG_PRIO_NONE)
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+void rkisp2_params_isr(struct rkisp2_params *params)
+{
+ struct rkisp2_params_buffer *buf;
+
+ spin_lock(¶ms->buf_lock);
+ buf = list_first_entry_or_null(¶ms->params,
+ struct rkisp2_params_buffer, queue);
+ if (buf)
+ list_del(&buf->queue);
+ spin_unlock(¶ms->buf_lock);
+
+ if (!buf)
+ return;
+
+ rkisp2_params_configure(params, buf, RKISP2_PARAMS_CONFIG_PRIO_NONE);
+}
+
+void rkisp2_params_pre_configure(struct rkisp2_params *params,
+ enum rkisp2_fmt_raw_pat_type bayer_pat)
+{
+ struct rkisp2_params_buffer *buf;
+
+ params->raw_type = bayer_pat;
+
+ spin_lock_irq(¶ms->buf_lock);
+ buf = list_first_entry_or_null(¶ms->params,
+ struct rkisp2_params_buffer, queue);
+ spin_unlock_irq(¶ms->buf_lock);
+
+ if (!buf)
+ return;
+
+ rkisp2_params_configure(params, buf, RKISP2_PARAMS_CONFIG_PRIO_PRE);
+}
+
+void rkisp2_params_post_configure(struct rkisp2_params *params)
+{
+ struct rkisp2_params_buffer *buf;
+
+ spin_lock_irq(¶ms->buf_lock);
+ buf = list_first_entry_or_null(¶ms->params,
+ struct rkisp2_params_buffer, queue);
+ if (buf)
+ list_del(&buf->queue);
+ spin_unlock_irq(¶ms->buf_lock);
+
+ if (!buf)
+ return;
+
+ rkisp2_params_configure(params, buf, RKISP2_PARAMS_CONFIG_PRIO_POST);
+}
+
+/*** V4L2 ***/
+
+static int rkisp2_params_enum_fmt_meta_out(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index)
+ return -EINVAL;
+
+ if (f->mbus_code && f->mbus_code != MEDIA_BUS_FMT_METADATA_FIXED)
+ return -EINVAL;
+
+ f->pixelformat = V4L2_META_FMT_RKISP2_PARAMS;
+
+ return 0;
+}
+
+static int rkisp2_params_g_fmt_meta_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+
+ static const struct v4l2_meta_format mfmt = {
+ .dataformat = V4L2_META_FMT_RKISP2_PARAMS,
+ .buffersize = v4l2_isp_params_buffer_size(RKISP2_PARAMS_MAX_SIZE),
+ };
+
+ f->fmt.meta = mfmt;
+
+ return 0;
+}
+
+static int rkisp2_params_querycap(struct file *file,
+ void *priv, struct v4l2_capability *cap)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ strscpy(cap->driver, RKISP2_DRIVER_NAME, sizeof(cap->driver));
+ strscpy(cap->card, vdev->name, sizeof(cap->card));
+ strscpy(cap->bus_info, RKISP2_BUS_INFO, sizeof(cap->bus_info));
+
+ return 0;
+}
+
+/* ISP params video device IOCTLs */
+static const struct v4l2_ioctl_ops rkisp2_params_ioctl = {
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_enum_fmt_meta_out = rkisp2_params_enum_fmt_meta_out,
+ .vidioc_g_fmt_meta_out = rkisp2_params_g_fmt_meta_out,
+ .vidioc_s_fmt_meta_out = rkisp2_params_g_fmt_meta_out,
+ .vidioc_try_fmt_meta_out = rkisp2_params_g_fmt_meta_out,
+ .vidioc_querycap = rkisp2_params_querycap,
+};
+
+static int rkisp2_params_vb2_queue_setup(struct vb2_queue *vq,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ /* \todo num_buffers? */
+
+ *num_planes = 1;
+
+ sizes[0] = v4l2_isp_params_buffer_size(RKISP2_PARAMS_MAX_SIZE);
+
+ return 0;
+}
+
+static int rkisp2_params_vb2_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkisp2_params_buffer *params_buf = to_rkisp2_params_buffer(vbuf);
+
+ params_buf->cfg = kvmalloc(v4l2_isp_params_buffer_size(RKISP2_PARAMS_MAX_SIZE),
+ GFP_KERNEL);
+ if (!params_buf->cfg)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void rkisp2_params_vb2_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkisp2_params_buffer *params_buf = to_rkisp2_params_buffer(vbuf);
+
+ kvfree(params_buf->cfg);
+ params_buf->cfg = NULL;
+}
+
+static void rkisp2_params_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkisp2_params_buffer *params_buf = to_rkisp2_params_buffer(vbuf);
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct rkisp2_params *params = vq->drv_priv;
+
+ spin_lock_irq(¶ms->buf_lock);
+ list_add_tail(¶ms_buf->queue, ¶ms->params);
+ spin_unlock_irq(¶ms->buf_lock);
+}
+
+static int rkisp2_params_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+ struct rkisp2_params *params = vb->vb2_queue->drv_priv;
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkisp2_params_buffer *params_buf = to_rkisp2_params_buffer(vbuf);
+ struct v4l2_isp_params_buffer *cfg = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
+ size_t payload_size = vb2_get_plane_payload(vb, 0);
+ int ret;
+
+ ret = v4l2_isp_params_validate_buffer_size(params->rkisp2->dev, vb,
+ v4l2_isp_params_buffer_size(RKISP2_PARAMS_MAX_SIZE));
+ if (ret)
+ return ret;
+
+ /*
+ * Copy the parameters buffer to the internal scratch buffer to avoid
+ * userspace modifying the buffer content while the driver processes it.
+ */
+ memcpy(params_buf->cfg, cfg, payload_size);
+
+ return v4l2_isp_params_validate_buffer(params->rkisp2->dev, vb, cfg,
+ rkisp2_params_block_types_info,
+ ARRAY_SIZE(rkisp2_params_block_types_info));
+}
+
+static void rkisp2_params_vb2_stop_streaming(struct vb2_queue *vq)
+{
+ struct rkisp2_params *params = vq->drv_priv;
+ struct rkisp2_params_buffer *buf;
+ LIST_HEAD(tmp_list);
+
+ /*
+ * we first move the buffers into a local list 'tmp_list'
+ * and then we can iterate it and call vb2_buffer_done
+ * without holding the lock
+ */
+ spin_lock_irq(¶ms->buf_lock);
+ list_splice_init(¶ms->params, &tmp_list);
+ spin_unlock_irq(¶ms->buf_lock);
+
+ list_for_each_entry(buf, &tmp_list, queue)
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+}
+
+static const struct vb2_ops rkisp2_params_vb2_ops = {
+ .queue_setup = rkisp2_params_vb2_queue_setup,
+ .buf_init = rkisp2_params_vb2_buf_init,
+ .buf_cleanup = rkisp2_params_vb2_buf_cleanup,
+ .buf_queue = rkisp2_params_vb2_buf_queue,
+ .buf_prepare = rkisp2_params_vb2_buf_prepare,
+ .stop_streaming = rkisp2_params_vb2_stop_streaming,
+};
+
+static const struct v4l2_file_operations rkisp2_params_fops = {
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = vb2_fop_poll,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release
+};
+
+static int rkisp2_params_link_validate(struct media_link *link)
+{
+ /* \todo implement this */
+ return 0;
+}
+
+static const struct media_entity_operations rkisp2_params_media_ops = {
+ .link_validate = rkisp2_params_link_validate,
+};
+
+static int rkisp2_params_init_vb2_queue(struct vb2_queue *q,
+ struct rkisp2_params *params)
+{
+ struct rkisp2_vdev_node *node;
+
+ node = container_of(q, struct rkisp2_vdev_node, buf_queue);
+
+ q->type = V4L2_BUF_TYPE_META_OUTPUT;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->drv_priv = params;
+ q->ops = &rkisp2_params_vb2_ops;
+ q->mem_ops = &vb2_vmalloc_memops;
+ q->buf_struct_size = sizeof(struct rkisp2_params_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &node->vlock;
+
+ return vb2_queue_init(q);
+}
+
+int rkisp2_params_register(struct rkisp2_device *rkisp2)
+{
+ struct rkisp2_params *params = &rkisp2->params;
+ struct rkisp2_vdev_node *node = ¶ms->vnode;
+ struct video_device *vdev = &node->vdev;
+ int ret;
+
+ params->rkisp2 = rkisp2;
+ mutex_init(&node->vlock);
+ INIT_LIST_HEAD(¶ms->params);
+ spin_lock_init(¶ms->buf_lock);
+
+ strscpy(vdev->name, RKISP2_PARAMS_DEV_NAME, sizeof(vdev->name));
+
+ video_set_drvdata(vdev, params);
+ vdev->ioctl_ops = &rkisp2_params_ioctl;
+ vdev->fops = &rkisp2_params_fops;
+ vdev->release = video_device_release_empty;
+ /*
+ * Provide a mutex to v4l2 core. It will be used
+ * to protect all fops and v4l2 ioctls.
+ */
+ vdev->lock = &node->vlock;
+ vdev->v4l2_dev = &rkisp2->v4l2_dev;
+ vdev->queue = &node->buf_queue;
+ vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_META_OUTPUT;
+ vdev->entity.ops = &rkisp2_params_media_ops;
+ vdev->vfl_dir = VFL_DIR_TX;
+ ret = rkisp2_params_init_vb2_queue(vdev->queue, params);
+ if (ret)
+ goto err_media;
+
+ video_set_drvdata(vdev, params);
+
+ node->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
+ if (ret)
+ goto err_media;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(rkisp2->dev,
+ "failed to register %s, ret=%d\n", vdev->name, ret);
+ return ret;
+ }
+
+ return 0;
+
+err_media:
+ media_entity_cleanup(&vdev->entity);
+ mutex_destroy(&node->vlock);
+ return ret;
+}
+
+void rkisp2_params_unregister(struct rkisp2_device *rkisp2)
+{
+ struct rkisp2_params *params = &rkisp2->params;
+ struct rkisp2_vdev_node *node = ¶ms->vnode;
+ struct video_device *vdev = &node->vdev;
+
+ if (!video_is_registered(vdev))
+ return;
+
+ vb2_video_unregister_device(vdev);
+ media_entity_cleanup(&vdev->entity);
+ mutex_destroy(&node->vlock);
+}
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v3x.h b/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v3x.h
index bb79d08d206f..d27d9a48887e 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v3x.h
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v3x.h
@@ -881,6 +881,15 @@
#define ISP3X_LSC_YSIZE_CD (ISP3X_LSC_BASE + 0x000A8)
#define ISP3X_LSC_YSIZE_EF (ISP3X_LSC_BASE + 0x000AC)
+#define ISP3X_LSC_XGRAD(n) (n < 4 ? (ISP3X_LSC_XGRAD_01 + (n) * 4) : \
+ (ISP3X_LSC_XGRAD_89 + ((n) - 4) * 4))
+#define ISP3X_LSC_YGRAD(n) (n < 4 ? (ISP3X_LSC_YGRAD_01 + (n) * 4) : \
+ (ISP3X_LSC_YGRAD_89 + ((n) - 4) * 4))
+#define ISP3X_LSC_XSIZE(n) (n < 4 ? (ISP3X_LSC_XSIZE_01 + (n) * 4) : \
+ (ISP3X_LSC_XSIZE_89 + ((n) - 4) * 4))
+#define ISP3X_LSC_YSIZE(n) (n < 4 ? (ISP3X_LSC_YSIZE_01 + (n) * 4) : \
+ (ISP3X_LSC_YSIZE_89 + ((n) - 4) * 4))
+
#define ISP3X_DEBAYER_BASE 0x00002500
#define ISP3X_DEBAYER_CONTROL (ISP3X_DEBAYER_BASE + 0x00000)
#define ISP3X_DEBAYER_G_INTERP (ISP3X_DEBAYER_BASE + 0x00004)
@@ -3235,6 +3244,12 @@
#define ISP33_IIR_WR_ID(x) (((x) & 0x3f) << 16)
#define ISP33_IIR_WR_CLEAR BIT(24)
+/* GAMMA_OUT */
+#define ISP3X_GAMMA_OUT_CTRL_EN BIT(0)
+#define ISP3X_GAMMA_OUT_CTRL_MODE_EQUIDISTANT BIT(1)
+#define ISP3X_GAMMA_OUT_CTRL_SEGMENTS_48 BIT(2)
+#define ISP3X_GAMMA_OUT_SAMPLE(a, b) (((a) & 0xfff) | (((b) & 0xfff) << 16))
+
/* HDRTMO */
/* HDRDRC */
@@ -3264,6 +3279,7 @@
#define ISP3X_DPCC_WORKING BIT(30)
/* CCM */
+#define ISP3X_CCM_COEFF(a, b) ((a & 0x7ff) | (b << 16))
#define ISP3X_CCM_HIGHY_ADJ_DIS BIT(1)
#define ISP32_CCM_ENH_ADJ_EN BIT(2)
#define ISP32_CCM_ASYM_ADJ_EN BIT(3)
@@ -3288,6 +3304,21 @@
#define ISP3X_LSC_TABLE_ADDRESS_0 0
#define ISP3X_LSC_TABLE_ADDRESS_153 153
+#define ISP3X_LSC_CTRL_EN BIT(0)
+
+#define ISP3X_LSC_ACTIVE_TABLE BIT(1)
+
+#define ISP3X_LSC_LUT_EN BIT(1)
+#define ISP3X_LSC_SECTOR_16X16 BIT(2)
+#define ISP3X_LSC_PRE_RD_ST_MODE BIT(4)
+
+#define ISP3X_LSC_TABLE_DATA(v0, v1) \
+ (((v0) & 0x1FFF) | (((v1) & 0x1FFF) << 16))
+#define ISP3X_LSC_SECT_SIZE(v0, v1) \
+ (((v0) & 0xFFF) | (((v1) & 0xFFF) << 16))
+#define ISP3X_LSC_GRAD_SIZE(v0, v1) \
+ (((v0) & 0xFFF) | (((v1) & 0xFFF) << 16))
+
#define ISP3X_LSC_LUT_EN BIT(1)
#define ISP3X_LSC_SECTOR_16X16 BIT(2)
#define ISP3X_LSC_PRE_RD_ST_MODE BIT(4)
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index a2b650f4ec3c..470e5b84ed15 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1467,6 +1467,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_META_FMT_RK_ISP1_PARAMS: descr = "Rockchip ISP1 3A Parameters"; break;
case V4L2_META_FMT_RK_ISP1_STAT_3A: descr = "Rockchip ISP1 3A Statistics"; break;
case V4L2_META_FMT_RK_ISP1_EXT_PARAMS: descr = "Rockchip ISP1 Ext 3A Params"; break;
+ case V4L2_META_FMT_RKISP2_PARAMS: descr = "Rockchip ISP2 3A Parameters"; break;
case V4L2_META_FMT_C3ISP_PARAMS: descr = "Amlogic C3 ISP Parameters"; break;
case V4L2_META_FMT_C3ISP_STATS: descr = "Amlogic C3 ISP Statistics"; break;
case V4L2_META_FMT_MALI_C55_PARAMS: descr = "ARM Mali-C55 ISP Parameters"; break;
diff --git a/include/uapi/linux/rkisp2-config.h b/include/uapi/linux/rkisp2-config.h
index ce8093db5998..0e42dbf2c9f2 100644
--- a/include/uapi/linux/rkisp2-config.h
+++ b/include/uapi/linux/rkisp2-config.h
@@ -8,6 +8,18 @@
#ifndef _UAPI_RKISP2_CONFIG_H
#define _UAPI_RKISP2_CONFIG_H
+#ifdef __KERNEL__
+#include <linux/build_bug.h>
+#endif /* __KERNEL__ */
+#include <linux/types.h>
+
+#include <linux/media/v4l2-isp.h>
+
+#define RKISP2_ISP_GAMMA_OUT_MAX_SEGMENTS 49
+
+#define RKISP2_ISP_LSC_SAMPLES_MAX 17
+#define RKISP2_ISP_LSC_SECTORS_TBL_SIZE_MAX 16
+
/**
* enum rkisp2_isp_version - ISP variants
*
@@ -17,4 +29,306 @@ enum rkisp2_isp_version {
RKISP3_V0 = 30,
};
+/* See enum rkisp2_isp_goc_segments for logarithmic segment sizes */
+enum rkisp2_isp_goc_mode {
+ RKISP2_ISP_GOC_MODE_LOGARITHMIC,
+ RKISP2_ISP_GOC_MODE_EQUIDISTANT
+};
+
+/*
+ * The segments are:
+ * 1 x8, 2 x4, 4 x4, 8 x4, 16 x4, 32 x4, 64 x4, 128 x4, 256 x4, 512 x4
+ * In 48-segment mode, the last group of 512 x4 becomes 256 x8
+ */
+enum rkisp2_isp_goc_segments {
+ RKISP2_ISP_GOC_SEGMENTS_44,
+ RKISP2_ISP_GOC_SEGMENTS_48
+};
+
+enum rkisp2_isp_lsc_config {
+ RKISP2_ISP_LSC_CONFIG_8X8,
+ RKISP2_ISP_LSC_CONFIG_16X16
+};
+
+enum rkisp2_isp_set_active_table_when {
+ RKISP2_ISP_LSC_SET_ACTIVE_TABLE_AFTER,
+ RKISP2_ISP_LSC_SET_ACTIVE_TABLE_BEFORE,
+};
+
+/*---------- Parameters ------------*/
+
+/**
+ * enum rkisp2_params_block_type - RkISP1 extensible params block type
+ *
+ * @RKISP2_PARAMS_BLOCK_BLS: Black level subtraction
+ * @RKISP2_PARAMS_BLOCK_AWB_GAINS: AWB gains
+ * @RKISP2_PARAMS_BLOCK_CSM: Color conversion coefficients (in the ISP block)
+ * @RKISP2_PARAMS_BLOCK_CCM: Color correction matrix (in the CCM block)
+ * @RKISP2_PARAMS_BLOCK_GOC: Gamma out correction
+ * @RKISP2_PARAMS_BLOCK_LSC: Lens shading correction
+ * */
+enum rkisp2_params_block_type {
+ RKISP2_PARAMS_BLOCK_BLS,
+ RKISP2_PARAMS_BLOCK_AWB_GAINS,
+ RKISP2_PARAMS_BLOCK_CSM,
+ RKISP2_PARAMS_BLOCK_CCM,
+ RKISP2_PARAMS_BLOCK_GOC,
+ RKISP2_PARAMS_BLOCK_LSC,
+};
+
+/**
+ * struct rkisp2_isp_window - measurement window.
+ *
+ * Measurements are calculated per window inside the frame.
+ * This struct represents a window for a measurement.
+ *
+ * @h_offs: the horizontal offset of the window from the left of the frame in pixels.
+ * @v_offs: the vertical offset of the window from the top of the frame in pixels.
+ * @h_size: the horizontal size of the window in pixels
+ * @v_size: the vertical size of the window in pixels.
+ */
+struct rkisp2_isp_window {
+ __u16 h_offs;
+ __u16 v_offs;
+ __u16 h_size;
+ __u16 v_size;
+};
+
+/**
+ * struct rkisp2_isp_bls_fixed_val - BLS fixed subtraction values
+ *
+ * These are signed 13-bit (-4096 to +4095).
+ *
+ * @a: Fixed black level value for Bayer channel 0
+ * @b: Fixed black level value for Bayer channel 1
+ * @c: Fixed black level value for Bayer channel 2
+ * @d: Fixed black level value for Bayer channel 3
+ */
+struct rkisp2_isp_bls_fixed_val {
+ __s16 a;
+ __s16 b;
+ __s16 c;
+ __s16 d;
+};
+
+/**
+ * struct rkisp2_isp_awb_gains - Auto white balance gain in the ISP block
+ *
+ * All fields in this struct are 16 bit, where:
+ * 0x100h = 1, unsigned integer value, range 0 to 63 with 8 bit fractional part.
+ *
+ * This leaves the upper two msb unaccounted for; it is unknown if these are
+ * unused or misdocumented.
+ *
+ * TODO investigate the upper two bits
+ *
+ * @r: gain value for red component.
+ * @gr: gain value for green component in red line.
+ * @b: gain value for blue component.
+ * @gb: gain value for green component in blue line.
+ */
+struct rkisp2_isp_awb_gains {
+ __u16 r;
+ __u16 gr;
+ __u16 b;
+ __u16 gb;
+};
+
+/**
+ * struct rkisp2_params_bls - RkISP2 params BLS config
+ *
+ * RkISP2 parameters Black Level Subtraction configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_BLS`.
+ *
+ * TODO Check if auto-mode and window selection is for both blocks or just for
+ * one block (it might be the same as 2.x)
+ *
+ * @header: The RkISP2 parameters block header
+ * @enable_auto: Automatic mode activated means that the measured values
+ * are subtracted. Otherwise the fixed subtraction
+ * values will be subtracted.
+ * @enabled_windows: enabled window (bit 0 for window 1, bit 1 for window 2)
+ * @bls_window1: Measurement window 1 size
+ * @bls_window2: Measurement window 2 size
+ * @bls_samples: Set amount of measured pixels for each Bayer position
+ * (A, B, C and D) to 2^bls_samples. (TODO needs confirmation)
+ * @bls_fixed_val: Black Level Subtraction fixed values for the BLS module at
+ * the front of the pipeline
+ * @bls1_fixed_val: Black Level Subtraction fixed values for the BLS module after
+ * bayer noise reduction
+ */
+struct rkisp2_params_bls {
+ struct v4l2_isp_params_block_header header;
+ __u8 enable_auto;
+ __u8 enabled_windows;
+ struct rkisp2_isp_window bls_window1;
+ struct rkisp2_isp_window bls_window2;
+ __u8 bls_samples;
+ struct rkisp2_isp_bls_fixed_val bls_fixed_val;
+ struct rkisp2_isp_bls_fixed_val bls1_fixed_val;
+} __attribute__((aligned(8)));
+
+/**
+ * struct rkisp2_params_awb_gains - RKISP2 params AWB gains config
+ *
+ * RkISP2 parameters auto white balance gains configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_AWB_GAINS`.
+ *
+ * TODO investigate what the different blocks mean
+ *
+ * Block 0 is equivalent to the awb gains block on 2.x, but blocks 1 and
+ * 2 do not exist on 2.x.
+ *
+ * @header: The RkISP2 parameters block header
+ * @gains: Gains configuration for block i
+ */
+struct rkisp2_params_awb_gains {
+ struct v4l2_isp_params_block_header header;
+ struct rkisp2_isp_awb_gains gains[3];
+} __attribute__((aligned(8)));
+
+/**
+ * struct rkisp2_params_csm - Configuration used by Color Space Conversion
+ *
+ * RkISP2 parameters histogram configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_CSM`.
+ *
+ * @header: The RkISP2 parameters block header
+ * @coeff: color correction matrix. Values are 9-bit signed fixed-point numbers with 2 bit integer
+ * and 7 bit fractional part, ranging from -2 (0x100) to +1.992 (0x0FF). 0 is
+ * represented by 0x000 and a coefficient value of 1 as 0x080.
+ */
+struct rkisp2_params_csm {
+ struct v4l2_isp_params_block_header header;
+ __u16 coeff[3][3];
+};
+
+/**
+ * struct rkisp2_params_ccm - Configuration used by Color Correction Matrix
+ *
+ * RkISP2 parameters histogram configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_CCM`.
+ *
+ * @header: The RkISP2 parameters block header
+ * @high_y_alpha_adj_en: Enable CCM high Y alpha adjustment (TODO figure out what this does)
+ * @coeff: color correction matrix. Values are 11-bit signed fixed-point numbers with 4 bit integer
+ * and 7 bit fractional part, ranging from -8 (0x400) to +7.992 (0x3FF). 0 is
+ * represented by 0x000 and a coefficient value of 1 as 0x080. The
+ * value is expanded 128 times (TODO figure out what this means).
+ * @offset: Red, Green, Blue offsets for the color correction matrix. 12-bits
+ * wide ranging from -4096 to 4095, but only for red; green and blue are 11-bit
+ * signed fixed-point like coeff, but are still 12-bits wide.
+ * @y_coeff: Red, Green, Blue coefficients for RGB2Y calculation. red and green
+ * are 11-bits wide and blue is 12-bits wide. The value is expanded 128 times.
+ * @alp: CCM curve y-axis point definition for ccm input pixel's luminance.
+ * 11-bit unsigned ranging from 0 to 1024. The value is expanded 128 times.
+ * @inflection_point: Inflection point of the ccm alpha interpolation curve.
+ * The inflection point is 2^inflection_point. Since the maximum y-value is
+ * 1024, the maximum value of this field is expected to be 10 (0xa), but the
+ * documentation says 4'b10.
+ */
+struct rkisp2_params_ccm {
+ struct v4l2_isp_params_block_header header;
+ __u8 high_y_alpha_adj_en;
+ __u16 coeff[3][3];
+ __u16 offset[3];
+ __u16 y_coeff[3];
+ __u16 alp[17];
+ __u8 inflection_point;
+};
+
+/**
+ * struct rkisp2_params_goc - Configuration used by Gamma Out correction
+ *
+ * RkISP2 parameters gamma out correction configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_GOC`.
+ *
+ * @header: The RkISP2 parameters block header
+ * @mode: goc mode (from enum rkisp2_isp_goc_mode)
+ * @segments: segments mode (from enum rkisp2_isp_goc_segments)
+ * @offset: offset value of the gamma out curve
+ * @gamma_y: gamma out curve y-axis for all color components
+ *
+ * The number of entries of @gamma_y depends on the segments mode. The entries
+ * are 12-bit unsigned.
+ */
+struct rkisp2_params_goc {
+ struct v4l2_isp_params_block_header header;
+ __u8 mode;
+ __u8 segments;
+ __u16 offset;
+ __u16 gamma_y[RKISP2_ISP_GAMMA_OUT_MAX_SEGMENTS];
+};
+
+/**
+ * struct rkisp2_params_lsc - Configuration used by Lens shading correction
+ *
+ * RkISP2 parameters lens shading correction configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_LSC`.
+ *
+ * The LSC module on the rkisp2 two tables: the 0th table and the 1th table.
+ * They can be programmed independently and (somewhat) simultaneously, and can be
+ * swapped by setting a single register. Hence the UAPI here is designed so
+ * that all these components can be controlled independently.
+ *
+ * In the first dimension of {r,gr,gb,b}_data_tbl we can designate which table
+ * to write the data to. write_table is then used to signal whether to write
+ * the data, and this can be controlled for both tables. active_table chooses
+ * which table to activate. set_active_table_when signals whether to set the
+ * active_table before or after programming the table. This allows
+ * optimizations such as setting a future table in one parameter buffer while
+ * swapping before setting it.
+ *
+ * This design gives us more control. For example, if we want to only program
+ * the 0th table without modifying the 1th table, we do not need to also
+ * populate the 1th table and we can use write_table to designate that we only
+ * want to program the 0th table. We can also swap tables without needing to
+ * re-populate the tables by setting active_table and unsetting write_table.
+ *
+ * {x,y}_sizes designates the grid of the LSC, and the table entries above
+ * correspond to the *vertices* of the grid. {x,y}_grads control the bilinear
+ * interpolation within the grid.
+ *
+ * @header: The RkISP2 parameters block header
+ * @r_data_tbl: Sample table red
+ * @gr_data_tbl: Sample table green (red)
+ * @gb_data_tbl: Sample table green (blue)
+ * @b_data_tbl: Sample table blue
+ * @write_table: Set to 1 to signal to write the respective table from above
+ * @active_table: Choose which of the two tables is active (0 or 1)
+ * @set_active_table_when: From rkisp2_isp_set_active_table_when; switch to the
+ * active table before or after programming the table
+ * @x_sizes: Sizes x
+ * @y_sizes: Sizes y
+ * @x_grads: Gradients x
+ * @y_grads: Gradients y
+ * @window_mode: From enum rkisp2_isp_lsc_config
+ */
+struct rkisp2_params_lsc {
+ struct v4l2_isp_params_block_header header;
+
+ __u16 r_data_tbl[2][RKISP2_ISP_LSC_SAMPLES_MAX][RKISP2_ISP_LSC_SAMPLES_MAX];
+ __u16 gr_data_tbl[2][RKISP2_ISP_LSC_SAMPLES_MAX][RKISP2_ISP_LSC_SAMPLES_MAX];
+ __u16 gb_data_tbl[2][RKISP2_ISP_LSC_SAMPLES_MAX][RKISP2_ISP_LSC_SAMPLES_MAX];
+ __u16 b_data_tbl[2][RKISP2_ISP_LSC_SAMPLES_MAX][RKISP2_ISP_LSC_SAMPLES_MAX];
+ __u8 write_table[2];
+ __u8 active_table;
+ __u8 set_active_table_when;
+
+ __u16 x_sizes[RKISP2_ISP_LSC_SECTORS_TBL_SIZE_MAX];
+ __u16 y_sizes[RKISP2_ISP_LSC_SECTORS_TBL_SIZE_MAX];
+ __u16 x_grads[RKISP2_ISP_LSC_SECTORS_TBL_SIZE_MAX];
+ __u16 y_grads[RKISP2_ISP_LSC_SECTORS_TBL_SIZE_MAX];
+
+ __u8 window_mode;
+};
+
+#define RKISP2_PARAMS_MAX_SIZE \
+ (sizeof(struct rkisp2_params_bls) +\
+ sizeof(struct rkisp2_params_awb_gains) +\
+ sizeof(struct rkisp2_params_csm) +\
+ sizeof(struct rkisp2_params_ccm) +\
+ sizeof(struct rkisp2_params_goc) +\
+ sizeof(struct rkisp2_params_lsc))
+
#endif /* _UAPI_RKISP2_CONFIG_H */
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index eda4492e40dc..1d418a752d89 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -876,6 +876,9 @@ struct v4l2_pix_format {
#define V4L2_META_FMT_RK_ISP1_STAT_3A v4l2_fourcc('R', 'K', '1', 'S') /* Rockchip ISP1 3A Statistics */
#define V4L2_META_FMT_RK_ISP1_EXT_PARAMS v4l2_fourcc('R', 'K', '1', 'E') /* Rockchip ISP1 3a Extensible Parameters */
+/* Vendor specific - used for RKISP2 camera sub-system */
+#define V4L2_META_FMT_RKISP2_PARAMS v4l2_fourcc('R', 'K', '2', 'P') /* Rockchip ISP2 Parameters */
+
/* Vendor specific - used for C3_ISP */
#define V4L2_META_FMT_C3ISP_PARAMS v4l2_fourcc('C', '3', 'P', 'M') /* Amlogic C3 ISP Parameters */
#define V4L2_META_FMT_C3ISP_STATS v4l2_fourcc('C', '3', 'S', 'T') /* Amlogic C3 ISP Statistics */
--
2.47.2
^ permalink raw reply related
* [RFC PATCH 5/5] media: rkisp2: Add statistics capture video node
From: Paul Elder @ 2026-04-24 17:58 UTC (permalink / raw)
To: laurent.pinchart
Cc: Paul Elder, michael.riesch, xuhf, stefan.klug, linux-media,
linux-arm-kernel, linux-rockchip, linux-kernel
In-Reply-To: <20260424175853.638202-1-paul.elder@ideasonboard.com>
Implement support for getting statistics out of the ISP by dequeueing
statistics buffers from rkisp2.
Support for configuring the statistics engines in the ISP is added as
well, implemented as configurations in parameter buffers.
Support for the following statistics engines is added:
- AE (auto exposure) lite
- Histogram lite
- Histogram big
- AWB (auto white balance)
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
---
I've kept the debug counters that helped me debug interrupts for the
stats engines.
---
.../media/platform/rockchip/rkisp2/Makefile | 3 +-
.../platform/rockchip/rkisp2/rkisp2-common.h | 56 ++
.../platform/rockchip/rkisp2/rkisp2-debug.c | 16 +
.../platform/rockchip/rkisp2/rkisp2-dev.c | 13 +
.../platform/rockchip/rkisp2/rkisp2-isp.c | 6 +
.../platform/rockchip/rkisp2/rkisp2-params.c | 238 +++++++++
.../rockchip/rkisp2/rkisp2-regs-v2x.h | 39 +-
.../rockchip/rkisp2/rkisp2-regs-v3x.h | 13 +
.../platform/rockchip/rkisp2/rkisp2-stats.c | 482 ++++++++++++++++++
drivers/media/v4l2-core/v4l2-ioctl.c | 1 +
include/uapi/linux/rkisp2-config.h | 233 ++++++++-
include/uapi/linux/videodev2.h | 1 +
12 files changed, 1098 insertions(+), 3 deletions(-)
create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-stats.c
diff --git a/drivers/media/platform/rockchip/rkisp2/Makefile b/drivers/media/platform/rockchip/rkisp2/Makefile
index 0fa014afcae4..8e323a560a39 100644
--- a/drivers/media/platform/rockchip/rkisp2/Makefile
+++ b/drivers/media/platform/rockchip/rkisp2/Makefile
@@ -5,7 +5,8 @@ rockchip-isp2-y := rkisp2-capture.o \
rkisp2-dev.o \
rkisp2-dmarx.o \
rkisp2-isp.o \
- rkisp2-params.o
+ rkisp2-params.o \
+ rkisp2-stats.o
rockchip-isp2-$(CONFIG_DEBUG_FS) += rkisp2-debug.o
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-common.h b/drivers/media/platform/rockchip/rkisp2/rkisp2-common.h
index 7473dae6c525..742954e8b569 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-common.h
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-common.h
@@ -91,6 +91,7 @@ enum rkisp2_isp_pad {
RKISP2_ISP_PAD_SINK_VIDEO,
RKISP2_ISP_PAD_SINK_PARAMS,
RKISP2_ISP_PAD_SOURCE_VIDEO,
+ RKISP2_ISP_PAD_SOURCE_STATS,
RKISP2_ISP_PAD_MAX
};
@@ -314,6 +315,46 @@ struct rkisp2_capture {
struct v4l2_rect crop;
};
+/*
+ * struct rkisp2_stats - ISP Statistics device
+ *
+ * @vnode: video node
+ * @rkisp2: pointer to the rkisp2 device
+ * @lock: locks the buffer list 'stat'
+ * @stat: queue of rkisp2_buffer
+ * @vdev_fmt: v4l2_format of the metadata format
+ * @imsc: interrupts that the stats is listening for (for stats 3a)
+ * @icr: interrupts that the stats has handled for the current frame (for stats 3a)
+ * @cur_buf: the current buffer to accumulate stats over multiple interrupts
+ *
+ * The imsc and icr fields are necessary to accumulate the stats because there
+ * seem to be separate interrupts in stats 3a for big modules and non-big
+ * modules. The conditions that cause separate interrupts are not yet fully
+ * understood, but is hypothesized to be big-mode & different window size
+ * compared to lite mode. We may need a similar construct for the core stats,
+ * but they have not been implemented yet (and they may not even exist).
+ *
+ * The imsc field is filled at v_start time to store what stats 3a interrupts
+ * we expect, then as they come in they are cleared by signalling in icr. The
+ * interrupt that fully sets icr (compared to imsc) will complete the stats
+ * buffer. On the next v_start any leftovers are cleared and logged as a
+ * counter to debugfs.
+ */
+struct rkisp2_stats {
+ struct rkisp2_vdev_node vnode;
+ struct rkisp2_device *rkisp2;
+
+ spinlock_t lock; /* locks the buffers list 'stats' */
+ struct list_head stat;
+ struct v4l2_format vdev_fmt;
+
+ u32 imsc;
+ u32 icr;
+ struct rkisp2_buffer *cur_buf;
+
+ unsigned int awb_window_offset;
+};
+
/*
* struct rkisp2_params - ISP input parameters device
*
@@ -342,9 +383,17 @@ struct rkisp2_debug {
unsigned long irq_delay;
unsigned long mipi_error;
unsigned long stats_error;
+ unsigned long stats3a_irq;
+ unsigned long stats_irq_delay;
unsigned long stop_timeout[2];
unsigned long frame_drop[2];
unsigned long complete_frames;
+ unsigned long stats3a_hist_ch0_count;
+ unsigned long stats3a_hist_ch1_count;
+ unsigned long stats3a_hist_ch2_count;
+ unsigned long stats3a_hist_big_count;
+ unsigned long stats3a_awb_count;
+ unsigned long stats3a_awb_done_count;
};
/*
@@ -366,6 +415,7 @@ struct rkisp2_debug {
* @capture_devs: capture devices
* @dmarx: ISP memory read device
* @params: ISP parameters metadata output device
+ * @stats: ISP statistics metadata capture device
* @pipe: media pipeline
* @stream_lock: serializes {start/stop}_streaming callbacks between the capture devices.
* @debug: debug params to be exposed on debugfs
@@ -388,6 +438,7 @@ struct rkisp2_device {
struct rkisp2_capture capture_devs[2];
struct rkisp2_dmarx dmarx;
struct rkisp2_params params;
+ struct rkisp2_stats stats;
struct media_pipeline pipe;
struct mutex stream_lock; /* serialize {start/stop}_streaming cb between capture devices */
struct rkisp2_debug debug;
@@ -478,6 +529,8 @@ irqreturn_t rkisp2_capture_isr(int irq, void *ctx);
irqreturn_t rkisp2_mipi_isr(int irq, void *ctx);
void rkisp2_dmarx_isr(struct rkisp2_device *rkisp2, u32 status);
void rkisp2_params_isr(struct rkisp2_params *params);
+void rkisp2_stats_isr_v_start(struct rkisp2_stats *);
+irqreturn_t rkisp2_stats_isr_3a(struct rkisp2_stats *);
/* register/unregisters functions of the entities */
int rkisp2_capture_devs_register(struct rkisp2_device *rkisp2);
@@ -494,6 +547,9 @@ void rkisp2_params_pre_configure(struct rkisp2_params *params,
enum rkisp2_fmt_raw_pat_type bayer_pat);
void rkisp2_params_post_configure(struct rkisp2_params *params);
+int rkisp2_stats_register(struct rkisp2_device *rkisp2);
+void rkisp2_stats_unregister(struct rkisp2_device *rkisp2);
+
#if IS_ENABLED(CONFIG_DEBUG_FS)
void rkisp2_debug_init(struct rkisp2_device *rkisp2);
void rkisp2_debug_cleanup(struct rkisp2_device *rkisp2);
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-debug.c b/drivers/media/platform/rockchip/rkisp2/rkisp2-debug.c
index 4f439bd9156b..92beae3530c4 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-debug.c
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-debug.c
@@ -191,6 +191,10 @@ void rkisp2_debug_init(struct rkisp2_device *rkisp2)
&debug->mipi_error);
debugfs_create_ulong("stats_error", 0444, debug->debugfs_dir,
&debug->stats_error);
+ debugfs_create_ulong("stats3a_irq", 0444, debug->debugfs_dir,
+ &debug->stats3a_irq);
+ debugfs_create_ulong("stats_irq_delay", 0444, debug->debugfs_dir,
+ &debug->stats_irq_delay);
debugfs_create_ulong("mp_stop_timeout", 0444, debug->debugfs_dir,
&debug->stop_timeout[RKISP2_MAINPATH]);
debugfs_create_ulong("sp_stop_timeout", 0444, debug->debugfs_dir,
@@ -201,6 +205,18 @@ void rkisp2_debug_init(struct rkisp2_device *rkisp2)
&debug->frame_drop[RKISP2_SELFPATH]);
debugfs_create_ulong("complete_frames", 0444, debug->debugfs_dir,
&debug->complete_frames);
+ debugfs_create_ulong("stats3a_hist_ch0_count", 0444, debug->debugfs_dir,
+ &debug->stats3a_hist_ch0_count);
+ debugfs_create_ulong("stats3a_hist_ch1_count", 0444, debug->debugfs_dir,
+ &debug->stats3a_hist_ch1_count);
+ debugfs_create_ulong("stats3a_hist_ch2_count", 0444, debug->debugfs_dir,
+ &debug->stats3a_hist_ch2_count);
+ debugfs_create_ulong("stats3a_hist_big_count", 0444, debug->debugfs_dir,
+ &debug->stats3a_hist_big_count);
+ debugfs_create_ulong("stats3a_awb_count", 0444, debug->debugfs_dir,
+ &debug->stats3a_awb_count);
+ debugfs_create_ulong("stats3a_awb_done_count", 0444, debug->debugfs_dir,
+ &debug->stats3a_awb_done_count);
debugfs_create_file("input_status", 0444, debug->debugfs_dir, rkisp2,
&rkisp2_debug_input_status_fops);
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-dev.c b/drivers/media/platform/rockchip/rkisp2/rkisp2-dev.c
index 0356ef2a1cf1..4042bf43d287 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-dev.c
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-dev.c
@@ -138,12 +138,21 @@ static int rkisp2_create_links(struct rkisp2_device *rkisp2)
if (ret)
return ret;
+ /* stats links */
+ ret = media_create_pad_link(&rkisp2->isp.sd.entity,
+ RKISP2_ISP_PAD_SOURCE_STATS,
+ &rkisp2->stats.vnode.vdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret)
+ return ret;
return 0;
}
static void rkisp2_entities_unregister(struct rkisp2_device *rkisp2)
{
+ rkisp2_stats_unregister(rkisp2);
rkisp2_params_unregister(rkisp2);
rkisp2_dmarx_unregister(rkisp2);
rkisp2_capture_devs_unregister(rkisp2);
@@ -170,6 +179,10 @@ static int rkisp2_entities_register(struct rkisp2_device *rkisp2)
if (ret)
goto error;
+ ret = rkisp2_stats_register(rkisp2);
+ if (ret)
+ goto error;
+
ret = rkisp2_create_links(rkisp2);
if (ret)
goto error;
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-isp.c b/drivers/media/platform/rockchip/rkisp2/rkisp2-isp.c
index 0967d5772bc9..f8f34f4f5919 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-isp.c
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-isp.c
@@ -812,6 +812,7 @@ int rkisp2_isp_register(struct rkisp2_device *rkisp2)
MEDIA_PAD_FL_MUST_CONNECT;
pads[RKISP2_ISP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK;
pads[RKISP2_ISP_PAD_SOURCE_VIDEO].flags = MEDIA_PAD_FL_SOURCE;
+ pads[RKISP2_ISP_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_pads_init(&sd->entity, RKISP2_ISP_PAD_MAX, pads);
if (ret)
@@ -876,6 +877,9 @@ irqreturn_t rkisp2_isp_isr(int irq, void *ctx)
if (!status)
return IRQ_NONE;
+ /* This is in a separate register so we need to check it separately */
+ rkisp2_stats_isr_3a(&rkisp2->stats);
+
/* Vertical sync signal, starting generating new frame */
if (status & RKISP2_CIF_ISP_V_START) {
rkisp2->isp.frame_sequence++;
@@ -884,6 +888,8 @@ irqreturn_t rkisp2_isp_isr(int irq, void *ctx)
WARN_ONCE(1, "irq delay is too long, buffers might not be in sync\n");
rkisp2->debug.irq_delay++;
}
+
+ rkisp2_stats_isr_v_start(&rkisp2->stats);
}
if (status & RKISP2_CIF_ISP_PIC_SIZE_ERROR) {
/* Clear pic_size_error */
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-params.c b/drivers/media/platform/rockchip/rkisp2/rkisp2-params.c
index b7b27d0e90c6..b149c20a4763 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-params.c
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-params.c
@@ -33,6 +33,10 @@
.priority = RKISP2_PARAMS_CONFIG_PRIO_ ## prio_postfix, \
}
+/*
+ * We only need one instance of hist_big here since it's only for accessing the
+ * members of the union
+ */
union rkisp2_params_block {
const struct v4l2_isp_params_block_header *header;
const struct rkisp2_params_bls *bls;
@@ -41,6 +45,10 @@ union rkisp2_params_block {
const struct rkisp2_params_ccm *ccm;
const struct rkisp2_params_goc *goc;
const struct rkisp2_params_lsc *lsc;
+ const struct rkisp2_params_ae_lite *ae_lite;
+ const struct rkisp2_params_hist_lite *hist_lite;
+ const struct rkisp2_params_hist_big *hist_big;
+ const struct rkisp2_params_awb_meas *awb_meas;
const __u8 *data;
};
@@ -56,6 +64,14 @@ static void rkisp2_params_goc(struct rkisp2_params *params,
union rkisp2_params_block block);
static void rkisp2_params_lsc(struct rkisp2_params *params,
union rkisp2_params_block block);
+static void rkisp2_params_ae_lite(struct rkisp2_params *params,
+ union rkisp2_params_block block);
+static void rkisp2_params_hist_lite(struct rkisp2_params *params,
+ union rkisp2_params_block block);
+static void rkisp2_params_hist_big(struct rkisp2_params *params,
+ union rkisp2_params_block block);
+static void rkisp2_params_awb_meas(struct rkisp2_params *params,
+ union rkisp2_params_block block);
typedef void (*rkisp2_params_handler)(struct rkisp2_params *params,
const union rkisp2_params_block block);
@@ -79,6 +95,12 @@ rkisp2_params_handlers[] = {
RKISP2_PARAMS_BLOCK_HANDLER_INFO(CCM, ccm, PRE),
RKISP2_PARAMS_BLOCK_HANDLER_INFO(GOC, goc, PRE),
RKISP2_PARAMS_BLOCK_HANDLER_INFO(LSC, lsc, POST),
+ RKISP2_PARAMS_BLOCK_HANDLER_INFO(AE_LITE, ae_lite, PRE),
+ RKISP2_PARAMS_BLOCK_HANDLER_INFO(HIST_LITE, hist_lite, PRE),
+ RKISP2_PARAMS_BLOCK_HANDLER_INFO(HIST_BIG0, hist_big, PRE),
+ RKISP2_PARAMS_BLOCK_HANDLER_INFO(HIST_BIG1, hist_big, PRE),
+ RKISP2_PARAMS_BLOCK_HANDLER_INFO(HIST_BIG2, hist_big, PRE),
+ RKISP2_PARAMS_BLOCK_HANDLER_INFO(AWB_MEAS, awb_meas, PRE),
};
static const struct v4l2_isp_params_block_type_info
@@ -89,6 +111,12 @@ rkisp2_params_block_types_info[] = {
RKISP2_PARAMS_BLOCK_INFO(CCM, ccm),
RKISP2_PARAMS_BLOCK_INFO(GOC, goc),
RKISP2_PARAMS_BLOCK_INFO(LSC, lsc),
+ RKISP2_PARAMS_BLOCK_INFO(AE_LITE, ae_lite),
+ RKISP2_PARAMS_BLOCK_INFO(HIST_LITE, hist_lite),
+ RKISP2_PARAMS_BLOCK_INFO(HIST_BIG0, hist_big),
+ RKISP2_PARAMS_BLOCK_INFO(HIST_BIG1, hist_big),
+ RKISP2_PARAMS_BLOCK_INFO(HIST_BIG2, hist_big),
+ RKISP2_PARAMS_BLOCK_INFO(AWB_MEAS, awb_meas),
};
static_assert(ARRAY_SIZE(rkisp2_params_handlers) ==
@@ -415,6 +443,216 @@ static void rkisp2_params_lsc(struct rkisp2_params *params,
rkisp2_param_set_bits(params, ISP3X_LSC_CTRL, data);
}
+static void rkisp2_params_ae_lite(struct rkisp2_params *params,
+ union rkisp2_params_block block)
+{
+ const struct rkisp2_params_ae_lite *arg = block.ae_lite;
+ u32 control;
+
+ if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp2_param_clear_bits(params, ISP_RAWAE_LITE_CTRL,
+ ISP3X_RAWAE_LITE_EN);
+ rkisp2_param_clear_bits(params, ISP_ISP3A_IMSC, ISP2X_3A_RAWAE_CH0);
+ return;
+ }
+
+ if (!(block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+ return;
+
+ /*
+ * \todo clamp these: width and height must be even, min size is 16x4,
+ * and total height must be < frame height
+ */
+ rkisp2_write(params->rkisp2, ISP_RAWAE_LITE_BLK_SIZ,
+ ISP3X_RAWAE_LITE_H(arg->meas_window.h_size) |
+ ISP3X_RAWAE_LITE_V(arg->meas_window.v_size));
+
+ rkisp2_write(params->rkisp2, ISP_RAWAE_LITE_OFFSET,
+ ISP3X_RAWAE_LITE_H(arg->meas_window.h_offs) |
+ ISP3X_RAWAE_LITE_V(arg->meas_window.v_offs));
+
+ control = arg->window_num ? ISP3X_RAWAE_LITE_WNDNUM : 0;
+ control |= ISP3X_RAWAE_LITE_EN;
+
+ rkisp2_param_set_bits(params, ISP_RAWAE_LITE_CTRL,
+ control);
+ /* TODO figure out CH1 and CH2 */
+ rkisp2_param_set_bits(params, ISP_ISP3A_IMSC, ISP2X_3A_RAWAE_CH0);
+}
+
+static void rkisp2_params_hist_lite(struct rkisp2_params *params,
+ union rkisp2_params_block block)
+{
+ const struct rkisp2_params_hist_lite *arg = block.hist_lite;
+ unsigned int i;
+ u32 control;
+
+ if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp2_param_clear_bits(params, ISP_RAWHIST_LITE_BASE, ISP_RAWHIST_CTRL_EN);
+ rkisp2_param_clear_bits(params, ISP_ISP3A_IMSC, ISP2X_3A_RAWHIST_CH0);
+ return;
+ }
+
+ if (!(block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+ return;
+
+ rkisp2_write(params->rkisp2, ISP_RAWHIST_LITE_SIZE,
+ ISP_RAWHIST_H_SIZE(arg->meas_window.h_size) |
+ ISP_RAWHIST_V_SIZE(arg->meas_window.v_size));
+
+ rkisp2_write(params->rkisp2, ISP_RAWHIST_LITE_OFFS,
+ ISP_RAWHIST_H_OFFS(arg->meas_window.h_offs) |
+ ISP_RAWHIST_V_OFFS(arg->meas_window.v_offs));
+
+ rkisp2_write(params->rkisp2, ISP_RAWHIST_LITE_RAW2Y_CC,
+ ISP_RAWHIST_RAW2Y_CC_RCC(arg->coeffs.r) |
+ ISP_RAWHIST_RAW2Y_CC_GCC(arg->coeffs.g) |
+ ISP_RAWHIST_RAW2Y_CC_BCC(arg->coeffs.b));
+
+ for (i = 0; i < 6; i++) {
+ rkisp2_write(params->rkisp2, ISP_RAWHIST_LITE_WEIGHT + 4 * i,
+ ISP_RAWHIST_LITE_WEIGHT_WND0(arg->weights[4 * i + 0]) |
+ ISP_RAWHIST_LITE_WEIGHT_WND1(arg->weights[4 * i + 1]) |
+ ISP_RAWHIST_LITE_WEIGHT_WND2(arg->weights[4 * i + 2]) |
+ ISP_RAWHIST_LITE_WEIGHT_WND3(arg->weights[4 * i + 3]));
+ }
+
+ rkisp2_write(params->rkisp2, ISP_RAWHIST_LITE_WEIGHT + 4 * 6,
+ ISP_RAWHIST_LITE_WEIGHT_WND0(arg->weights[24]));
+
+ control = ISP_RAWHIST_CTRL_EN |
+ ISP_RAWHIST_CTRL_STEPSIZE(arg->stepsize) |
+ ISP_RAWHIST_CTRL_MODE(arg->mode) |
+ ISP_RAWHIST_CTRL_WATERLINE(arg->waterline) |
+ ISP_RAWHIST_CTRL_DATA_SEL(arg->data_sel);
+ rkisp2_write(params->rkisp2, ISP_RAWHIST_LITE_CTRL, control);
+ // I think you can choose the channels in VI_ISP_PATH
+ rkisp2_param_set_bits(params, ISP_ISP3A_IMSC, ISP2X_3A_RAWHIST_CH0);
+}
+
+static void rkisp2_params_hist_big(struct rkisp2_params *params,
+ union rkisp2_params_block block)
+{
+ const struct rkisp2_params_hist_big *arg = block.hist_big;
+ unsigned int i, reg_base, ctrl;
+ u32 control;
+
+ switch (block.header->type) {
+ case RKISP2_PARAMS_BLOCK_HIST_BIG0:
+ reg_base = ISP_RAWHIST_BIG1_BASE;
+ break;
+ case RKISP2_PARAMS_BLOCK_HIST_BIG1:
+ reg_base = ISP_RAWHIST_BIG2_BASE;
+ break;
+ case RKISP2_PARAMS_BLOCK_HIST_BIG2:
+ reg_base = ISP_RAWHIST_BIG3_BASE;
+ break;
+ default:
+ WARN_ONCE(1, "invalid big histogram base %x\n", reg_base);
+ return;
+ }
+
+ ctrl = reg_base + ISP_RAWHIST_BIG_CTRL;
+
+ if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp2_param_clear_bits(params, ctrl, ISP_RAWHIST_CTRL_EN);
+ rkisp2_param_clear_bits(params, ISP_ISP3A_IMSC, ISP2X_3A_RAWHIST_BIG);
+ return;
+ }
+
+ if (!(block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+ return;
+
+ rkisp2_write(params->rkisp2, reg_base + ISP_RAWHIST_BIG_SIZE,
+ ISP_RAWHIST_H_SIZE(arg->meas_window.h_size) |
+ ISP_RAWHIST_V_SIZE(arg->meas_window.v_size));
+
+ rkisp2_write(params->rkisp2, reg_base + ISP_RAWHIST_BIG_OFFS,
+ ISP_RAWHIST_H_OFFS(arg->meas_window.h_offs) |
+ ISP_RAWHIST_V_OFFS(arg->meas_window.v_offs));
+
+ rkisp2_write(params->rkisp2, reg_base + ISP_RAWHIST_BIG_RAW2Y_CC,
+ ISP_RAWHIST_RAW2Y_CC_RCC(arg->coeffs.r) |
+ ISP_RAWHIST_RAW2Y_CC_GCC(arg->coeffs.g) |
+ ISP_RAWHIST_RAW2Y_CC_BCC(arg->coeffs.b));
+
+ /*
+ * TODO check if the other weights are programmed in the registers
+ * after the bins, or if it's sequential write
+ */
+ for (i = 0; i < 16; i++) {
+ rkisp2_write(params->rkisp2, reg_base + ISP_RAWHIST_BIG_WEIGHT_BASE + 4 * i,
+ ISP_RAWHIST_BIG_WEIGHT_WND0(arg->weights[5 * i + 0]) |
+ ISP_RAWHIST_BIG_WEIGHT_WND1(arg->weights[5 * i + 1]) |
+ ISP_RAWHIST_BIG_WEIGHT_WND2(arg->weights[5 * i + 2]) |
+ ISP_RAWHIST_BIG_WEIGHT_WND3(arg->weights[5 * i + 3]) |
+ ISP_RAWHIST_BIG_WEIGHT_WND4(arg->weights[5 * i + 4]));
+ }
+
+ rkisp2_param_set_bits(params, ISP_CTRL1, ISP21_BIGMODE_MODE | ISP21_BIGMODE_FORCE_EN);
+
+ control = ISP_RAWHIST_CTRL_EN |
+ ISP_RAWHIST_CTRL_STEPSIZE(arg->stepsize) |
+ ISP_RAWHIST_CTRL_MODE(arg->mode) |
+ ISP_RAWHIST_CTRL_WATERLINE(arg->waterline) |
+ ISP_RAWHIST_CTRL_DATA_SEL(arg->data_sel) |
+ (arg->window_num & ISP_RAWHIST_BIG_CTRL_WND_MASK);
+ rkisp2_write(params->rkisp2, ctrl, control);
+ rkisp2_param_set_bits(params, ISP_ISP3A_IMSC, ISP2X_3A_RAWHIST_BIG);
+}
+
+static void rkisp2_params_awb_meas(struct rkisp2_params *params,
+ union rkisp2_params_block block)
+{
+ const struct rkisp2_params_awb_meas *arg = block.awb_meas;
+ unsigned int i;
+ u32 val;
+ u32 control = 0;
+
+ if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp2_param_clear_bits(params, ISP3X_RAWAWB_CTRL,
+ ISP3X_RAWAWB_CTRL_EN);
+ rkisp2_param_clear_bits(params, ISP_ISP3A_IMSC, ISP2X_3A_RAWAWB);
+ rkisp2_param_clear_bits(params, ISP21_RAWAWB_BLK_CTRL, 0x1);
+ return;
+ }
+
+ if (!(block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+ return;
+
+ rkisp2_write(params->rkisp2, ISP21_RAWAWB_WIN_OFFS,
+ ISP3X_RAWAWB_WIN(arg->meas_window.h_offs,
+ arg->meas_window.v_offs));
+ rkisp2_write(params->rkisp2, ISP21_RAWAWB_WIN_SIZE,
+ ISP3X_RAWAWB_WIN(arg->meas_window.h_size,
+ arg->meas_window.v_size));
+
+ rkisp2_write(params->rkisp2, ISP21_RAWAWB_LIMIT_RG_MAX,
+ ISP3X_RAWAWB_LIMITS(arg->limits[1].r, arg->limits[1].g));
+ rkisp2_write(params->rkisp2, ISP21_RAWAWB_LIMIT_BY_MAX,
+ ISP3X_RAWAWB_LIMITS(arg->limits[1].b, arg->limits[1].y));
+ rkisp2_write(params->rkisp2, ISP21_RAWAWB_LIMIT_RG_MIN,
+ ISP3X_RAWAWB_LIMITS(arg->limits[0].r, arg->limits[0].g));
+ rkisp2_write(params->rkisp2, ISP21_RAWAWB_LIMIT_BY_MIN,
+ ISP3X_RAWAWB_LIMITS(arg->limits[0].b, arg->limits[0].y));
+
+ for (i = 0; i < RKISP2_ISP_AWB_COUNTS_SIZE / 5; i++) {
+ val = (arg->weights[5 * i] & 0x3f)
+ | ((arg->weights[5 * i + 1] & 0x3f) << 6)
+ | ((arg->weights[5 * i + 2] & 0x3f) << 12)
+ | ((arg->weights[5 * i + 3] & 0x3f) << 18)
+ | ((arg->weights[5 * i + 4] & 0x3f) << 24);
+ rkisp2_write(params->rkisp2, ISP21_RAWAWB_WRAM_DATA_BASE, val);
+ }
+
+ // This options looks like it's required to get "useful" data out
+ rkisp2_param_set_bits(params, ISP21_RAWAWB_BLK_CTRL, 0x1);
+
+ control |= ISP3X_RAWAWB_CTRL_EN;
+ rkisp2_write(params->rkisp2, ISP3X_RAWAWB_CTRL, control);
+ rkisp2_param_set_bits(params, ISP_ISP3A_IMSC, ISP2X_3A_RAWAWB);
+}
+
static void rkisp2_params_configure(struct rkisp2_params *params,
struct rkisp2_params_buffer *buf,
enum rkisp2_params_configure_priority prio)
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v2x.h b/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v2x.h
index 883bdb7c2a61..26deacb0b63d 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v2x.h
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v2x.h
@@ -1429,7 +1429,7 @@
#define ISP_RAWAE_LITE_BLK_SIZ (ISP_RAWAE_LITE_BASE + 0x00004)
#define ISP_RAWAE_LITE_OFFSET (ISP_RAWAE_LITE_BASE + 0x00008)
#define ISP_RAWAE_LITE_R2Y_CC (ISP_RAWAE_LITE_BASE + 0x0000c)
-#define ISP_RAWAE_LITE_RO_MEAN (ISP_RAWAE_LITE_BASE + 0x00010)
+#define ISP_RAWAE_LITE_RO_MEAN(i) (ISP_RAWAE_LITE_BASE + 0x00010 + ((i) * 4))
#define ISP_RAWAE_LITE_RO_DBG1 (ISP_RAWAE_LITE_BASE + 0x00074)
#define ISP_RAWAE_LITE_RO_DBG2 (ISP_RAWAE_LITE_BASE + 0x00078)
@@ -2585,4 +2585,41 @@
/* ISP21 DHAZ/DRC/BAY3D */
#define ISP21_SELF_FORCE_UPD BIT(31)
+/* RAWHIST */
+#define ISP_RAWHIST_CTRL_EN BIT(0)
+#define ISP_RAWHIST_CTRL_STEPSIZE(a) (((a) & 0x7) << 1)
+#define ISP_RAWHIST_CTRL_MODE(a) (((a) & 0x7) << 8)
+#define ISP_RAWHIST_CTRL_WATERLINE(a) (((a) & 0xfff) << 12)
+#define ISP_RAWHIST_CTRL_DATA_SEL(a) (((a) & 0x7) << 24)
+#define ISP_RAWHIST_CTRL_MEAS_DONE BIT(31)
+#define ISP_RAWHIST_BIG_CTRL_WND_5X5_1 BIT(28)
+#define ISP_RAWHIST_BIG_CTRL_WND_15X15_0 BIT(29)
+#define ISP_RAWHIST_BIG_CTRL_WND_15X15_1 (0x3 << 28)
+#define ISP_RAWHIST_BIG_CTRL_WND_MASK GENMASK(29, 28)
+
+#define ISP_RAWHIST_H_SIZE(a) ((a) & 0x7ff)
+#define ISP_RAWHIST_V_SIZE(a) (((a) & 0x7ff) << 16)
+#define ISP_RAWHIST_H_OFFS(a) ((a) & 0x1fff)
+#define ISP_RAWHIST_V_OFFS(a) (((a) & 0x1fff) << 16)
+
+#define ISP_RAWHIST_RAW2Y_CC_RCC(a) ((a) & 0xff)
+#define ISP_RAWHIST_RAW2Y_CC_GCC(a) (((a) & 0xff) << 8)
+#define ISP_RAWHIST_RAW2Y_CC_BCC(a) (((a) & 0xff) << 16)
+#define ISP_RAWHIST_RAW2Y_CC_OFF(a) (((a) & 0xff) << 24)
+
+#define ISP_RAWHIST_LITE_WEIGHT_WND0(a) ((a) & 0x3f)
+#define ISP_RAWHIST_LITE_WEIGHT_WND1(a) (((a) & 0x3f) << 8)
+#define ISP_RAWHIST_LITE_WEIGHT_WND2(a) (((a) & 0x3f) << 16)
+#define ISP_RAWHIST_LITE_WEIGHT_WND3(a) (((a) & 0x3f) << 24)
+
+#define ISP_RAWHIST_BIG_WEIGHT_WND0(a) ((a) & 0x3f)
+#define ISP_RAWHIST_BIG_WEIGHT_WND1(a) (((a) & 0x3f) << 6)
+#define ISP_RAWHIST_BIG_WEIGHT_WND2(a) (((a) & 0x3f) << 12)
+#define ISP_RAWHIST_BIG_WEIGHT_WND3(a) (((a) & 0x3f) << 18)
+#define ISP_RAWHIST_BIG_WEIGHT_WND4(a) (((a) & 0x3f) << 24)
+
+#define ISP_RAWHIST_BIN_MASK GENMASK(27, 0)
+
+#define ISP_RAWHIST_RAM_OFFSET(a) ((a) & 0xff)
+
#endif /* _RKISP_REGS_V2X_H */
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v3x.h b/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v3x.h
index d27d9a48887e..73dc3b22873c 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v3x.h
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v3x.h
@@ -3367,6 +3367,13 @@
#define ISP3X_RAWAE_BIG_WND3_EN BIT(6)
#define ISP3X_RAWAE_BIG_WND4_EN BIT(7)
+#define ISP3X_RAWAE_LITE_H(a) ((a) & 0x1fff)
+#define ISP3X_RAWAE_LITE_V(a) (((a) & 0x1fff) << 16)
+
+#define ISP3X_RAWAE_LITE_RO_MEAN_R(a) (((a) >> 22) & 0x3ff)
+#define ISP3X_RAWAE_LITE_RO_MEAN_B(a) (((a) >> 12) & 0x3ff)
+#define ISP3X_RAWAE_LITE_RO_MEAN_G(a) ((a) & 0xfff)
+
/* RAWHIST */
#define ISP3X_RAWHIST_EN BIT(0)
#define ISP3X_RAWHIST_STEPSIZE(x) (((x) & 0x7) << 1)
@@ -3397,6 +3404,12 @@
#define ISP3X_RAWAF_INTLINE0_EN BIT(27)
/* RAWAWB */
+#define ISP3X_RAWAWB_CTRL_EN BIT(0)
+#define ISP3X_RAWAWB_CTRL_MEAS_DONE BIT(31)
+
+#define ISP3X_RAWAWB_WIN(h, v) (((h) & 0x1fff) | (((v) & 0x1fff) << 16))
+#define ISP3X_RAWAWB_LIMITS(a, b) (((a) & 0xff) | (((b) & 0xff) << 16))
+
#define ISP32_RAWAWB_2DDR_PATH_EN BIT(23)
#define ISP32_RAWAWB_2DDR_PATH_DS BIT(27)
#define ISP32_RAWAWB_2DDR_PATH_ERR BIT(29)
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-stats.c b/drivers/media/platform/rockchip/rkisp2/rkisp2-stats.c
new file mode 100644
index 000000000000..f9dbe05bf218
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-stats.c
@@ -0,0 +1,482 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR MIT)
+/*
+ * Rockchip ISP2 Driver - Stats subdevice
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2026 Ideas on Board Oy.
+ */
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "rkisp2-common.h"
+
+#define RKISP2_STATS_DEV_NAME RKISP2_DRIVER_NAME "_stats"
+
+static int rkisp2_stats_enum_fmt_meta_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index)
+ return -EINVAL;
+
+ f->pixelformat = V4L2_META_FMT_RKISP2_STATS;
+
+ return 0;
+}
+
+static int rkisp2_stats_g_fmt_meta_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ static const struct v4l2_meta_format mfmt = {
+ .dataformat = V4L2_META_FMT_RKISP2_STATS,
+ .buffersize = sizeof(struct rkisp2_stats_buffer)
+ };
+
+ f->fmt.meta = mfmt;
+
+ return 0;
+}
+
+static int rkisp2_stats_querycap(struct file *file,
+ void *priv, struct v4l2_capability *cap)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ strscpy(cap->driver, RKISP2_DRIVER_NAME, sizeof(cap->driver));
+ strscpy(cap->card, vdev->name, sizeof(cap->card));
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops rkisp2_stats_ioctl = {
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_enum_fmt_meta_cap = rkisp2_stats_enum_fmt_meta_cap,
+ .vidioc_g_fmt_meta_cap = rkisp2_stats_g_fmt_meta_cap,
+ .vidioc_s_fmt_meta_cap = rkisp2_stats_g_fmt_meta_cap,
+ .vidioc_try_fmt_meta_cap = rkisp2_stats_g_fmt_meta_cap,
+ .vidioc_querycap = rkisp2_stats_querycap,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_file_operations rkisp2_stats_fops = {
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = vb2_fop_poll,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release
+};
+
+static int rkisp2_stats_vb2_queue_setup(struct vb2_queue *vq,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ /* TODO num_buffers */
+
+ *num_planes = 1;
+
+ sizes[0] = sizeof(struct rkisp2_stats_buffer);
+
+ return 0;
+}
+
+static void rkisp2_stats_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkisp2_buffer *stats_buf =
+ container_of(vbuf, struct rkisp2_buffer, vb);
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct rkisp2_stats *stats_dev = vq->drv_priv;
+
+ spin_lock_irq(&stats_dev->lock);
+ list_add_tail(&stats_buf->queue, &stats_dev->stat);
+ spin_unlock_irq(&stats_dev->lock);
+}
+
+static int rkisp2_stats_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+ if (vb2_plane_size(vb, 0) < sizeof(struct rkisp2_stats_buffer))
+ return -EINVAL;
+
+ vb2_set_plane_payload(vb, 0, sizeof(struct rkisp2_stats_buffer));
+
+ return 0;
+}
+
+static int rkisp2_stats_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct rkisp2_stats *stats = vq->drv_priv;
+ stats->imsc = 0;
+ stats->icr = 0;
+ stats->cur_buf = NULL;
+ stats->awb_window_offset = 0;
+
+ return 0;
+}
+
+static void rkisp2_stats_vb2_stop_streaming(struct vb2_queue *vq)
+{
+ struct rkisp2_stats *stats = vq->drv_priv;
+ struct rkisp2_buffer *buf;
+ LIST_HEAD(tmp_list);
+
+ spin_lock_irq(&stats->lock);
+ list_splice_init(&stats->stat, &tmp_list);
+ spin_unlock_irq(&stats->lock);
+
+ list_for_each_entry(buf, &tmp_list, queue)
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+
+ if (stats->cur_buf != NULL) {
+ vb2_buffer_done(&stats->cur_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ stats->cur_buf = NULL;
+ }
+}
+
+static const struct vb2_ops rkisp2_stats_vb2_ops = {
+ .queue_setup = rkisp2_stats_vb2_queue_setup,
+ .buf_queue = rkisp2_stats_vb2_buf_queue,
+ .buf_prepare = rkisp2_stats_vb2_buf_prepare,
+ .start_streaming = rkisp2_stats_vb2_start_streaming,
+ .stop_streaming = rkisp2_stats_vb2_stop_streaming,
+};
+
+static int
+rkisp2_stats_init_vb2_queue(struct vb2_queue *q, struct rkisp2_stats *stats)
+{
+ struct rkisp2_vdev_node *node;
+
+ node = container_of(q, struct rkisp2_vdev_node, buf_queue);
+
+ q->type = V4L2_BUF_TYPE_META_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->drv_priv = stats;
+ q->ops = &rkisp2_stats_vb2_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct rkisp2_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &node->vlock;
+ q->dev = stats->rkisp2->dev;
+
+ return vb2_queue_init(q);
+}
+
+static void rkisp2_stats_get_rawae_lite(struct rkisp2_stats *stats,
+ u32 status,
+ struct rkisp2_stats_buffer *pbuf)
+{
+ struct rkisp2_device *rkisp2 = stats->rkisp2;
+ struct rkisp2_isp_ae_lite *ae_lite;
+ unsigned int i;
+ u32 val;
+
+ /* TODO figure out a nicer way to synchronize this with params and imsc */
+ /* TODO figure out what the other channels are for */
+ if (!(status & ISP2X_3A_RAWAE_CH0))
+ return;
+ stats->icr |= ISP2X_3A_RAWAE_CH0;
+
+ ae_lite = &pbuf->ae_lite;
+
+ for (i = 0; i < RKISP2_ISP_AE_MEAN_MAX_LITE; i++) {
+ val = rkisp2_read(rkisp2, ISP_RAWAE_LITE_RO_MEAN(i));
+ ae_lite->exp_mean_r[i] = ISP3X_RAWAE_LITE_RO_MEAN_R(val);
+ ae_lite->exp_mean_g[i] = ISP3X_RAWAE_LITE_RO_MEAN_G(val);
+ ae_lite->exp_mean_b[i] = ISP3X_RAWAE_LITE_RO_MEAN_B(val);
+ }
+
+ /*
+ * The done bit is never set in the register; set it here to signal
+ * done to userspace
+ */
+ ae_lite->done = 1;
+}
+
+static void rkisp2_stats_hist_read(struct rkisp2_stats *stats, u32 reg_base,
+ struct rkisp2_isp_hist *hist)
+{
+ struct rkisp2_device *rkisp2 = stats->rkisp2;
+ unsigned int i;
+ u32 val, done;
+ unsigned int ctrl = reg_base + ISP_RAWHIST_BIG_CTRL;
+
+ /*
+ * Unlike rawae lite, this actually does get set, and we need to use it
+ * to determine which big block to process
+ */
+ done = rkisp2_read(rkisp2, ctrl) & ISP_RAWHIST_CTRL_MEAS_DONE;
+ hist->done = done ? 1 : 0;
+ if (!done)
+ return;
+
+ rkisp2_write(rkisp2, reg_base + ISP_RAWHIST_BIG_HRAM_CTRL,
+ ISP_RAWHIST_RAM_OFFSET(0));
+
+ for (i = 0; i < RKISP2_ISP_HIST_BIN_N_MAX; i++) {
+ val = rkisp2_read(rkisp2, reg_base + ISP_RAWHIST_BIG_RO_BASE_BIN);
+ hist->hist_bins[i] = val;
+ }
+
+ /* Set the done bit */
+ val = rkisp2_read(rkisp2, ctrl);
+ rkisp2_write(rkisp2, ctrl, val | ISP_RAWHIST_CTRL_MEAS_DONE);
+}
+
+static void rkisp2_stats_get_hist(struct rkisp2_stats *stats,
+ u32 status,
+ struct rkisp2_stats_buffer *pbuf)
+{
+ /*
+ * It seems that big only triggers the big interrupt, and lite only
+ * triggers the ch0 interrupts. ch1 and ch2 interrupts are therefore
+ * unknown, and we need to check the done bits of every big module to
+ * determine which to process
+ */
+
+ /*
+ * It looks like we get separate interrupts for big in big-mode (ie.
+ * using the 15x15 grid) and for non-big stuff
+ */
+
+ if (status & ISP2X_3A_RAWHIST_CH0) {
+ stats->rkisp2->debug.stats3a_hist_ch0_count++;
+ stats->icr |= ISP2X_3A_RAWHIST_CH0;
+ rkisp2_stats_hist_read(stats, ISP_RAWHIST_LITE_BASE, &pbuf->hist_lite);
+ }
+
+ if (status & ISP2X_3A_RAWHIST_BIG) {
+ stats->rkisp2->debug.stats3a_hist_big_count++;
+ stats->icr |= ISP2X_3A_RAWHIST_BIG;
+ rkisp2_stats_hist_read(stats, ISP_RAWHIST_BIG1_BASE, &pbuf->hist_big0);
+ rkisp2_stats_hist_read(stats, ISP_RAWHIST_BIG2_BASE, &pbuf->hist_big1);
+ rkisp2_stats_hist_read(stats, ISP_RAWHIST_BIG3_BASE, &pbuf->hist_big2);
+ }
+
+}
+
+static void rkisp2_stats_get_rawawb(struct rkisp2_stats *stats,
+ u32 status,
+ struct rkisp2_stats_buffer *pbuf)
+{
+ struct rkisp2_device *rkisp2 = stats->rkisp2;
+ struct rkisp2_isp_awb *awb = &pbuf->awb;
+ unsigned int ctrl = ISP21_RAWAWB_CTRL;
+ unsigned int i;
+ u32 val1, val2;
+ u32 done;
+
+ if (!(status & ISP2X_3A_RAWAWB))
+ return;
+ stats->icr |= ISP2X_3A_RAWAWB;
+
+ rkisp2->debug.stats3a_awb_count++;
+
+ done = rkisp2_read(rkisp2, ctrl) & ISP3X_RAWAWB_CTRL_MEAS_DONE;
+ awb->done = done ? 1 : 0;
+ if (!done)
+ return;
+
+ rkisp2->debug.stats3a_awb_done_count++;
+
+ for (i = 0; i < RKISP2_ISP_AWB_COUNTS_SIZE; i++) {
+ val1 = rkisp2_read(rkisp2, ISP21_RAWAWB_RAM_DATA_BASE);
+ val2 = rkisp2_read(rkisp2, ISP21_RAWAWB_RAM_DATA_BASE);
+ awb->counts_r[i] = (val2 >> 4) & 0x3ffff;
+ awb->counts_g[i] = (val1 >> 18) | ((val2 & 0xf) << 14);
+ awb->counts_b[i] = val1 & 0x3ffff;
+ awb->counts_w[i] = val2 >> 22;
+ }
+
+ /*
+ * Clear the done bit (reference says write 0 but writing 1 seems to be
+ * the correct reset, plus all the other stats 3a write 1 to reset)
+ */
+ val1 = rkisp2_read(rkisp2, ctrl);
+ rkisp2_write(rkisp2, ctrl, val1 | ISP3X_RAWAWB_CTRL_MEAS_DONE);
+}
+
+/* This is always called in an intterupt context */
+static struct rkisp2_buffer *rkisp2_stats_get_buf(struct rkisp2_stats *stats)
+{
+ struct rkisp2_buffer *ret = NULL;
+
+ if (stats->cur_buf != NULL)
+ return stats->cur_buf;
+
+ /* get one empty buffer */
+ if (!list_empty(&stats->stat)) {
+ ret = list_first_entry(&stats->stat, struct rkisp2_buffer, queue);
+ list_del(&ret->queue);
+ }
+
+ stats->cur_buf = ret;
+
+ return ret;
+}
+
+static void rkisp2_stats_complete_buf(struct rkisp2_stats *stats, struct rkisp2_buffer *buf)
+{
+ unsigned int frame_sequence = stats->rkisp2->isp.frame_sequence;
+ u64 timestamp = ktime_get_ns();
+
+ vb2_set_plane_payload(&buf->vb.vb2_buf, 0,
+ sizeof(struct rkisp2_stats_buffer));
+ buf->vb.sequence = frame_sequence;
+ buf->vb.vb2_buf.timestamp = timestamp;
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+
+ stats->cur_buf = NULL;
+}
+
+static void rkisp2_stats_send_measurement_3a(struct rkisp2_stats *stats, u32 status)
+{
+ struct rkisp2_stats_buffer *cur_stat_buf;
+ struct rkisp2_buffer *cur_buf;
+
+ cur_buf = rkisp2_stats_get_buf(stats);
+ if (!cur_buf)
+ return;
+
+ cur_stat_buf = (struct rkisp2_stats_buffer *)
+ vb2_plane_vaddr(&cur_buf->vb.vb2_buf, 0);
+
+ rkisp2_stats_get_rawae_lite(stats, status, cur_stat_buf);
+
+ rkisp2_stats_get_hist(stats, status, cur_stat_buf);
+
+ rkisp2_stats_get_rawawb(stats, status, cur_stat_buf);
+
+ rkisp2_write(stats->rkisp2, ISP_ISP3A_ICR, stats->icr);
+
+ if (stats->imsc == stats->icr) {
+ rkisp2_stats_complete_buf(stats, cur_buf);
+ stats->icr = 0;
+ }
+}
+
+void rkisp2_stats_isr_v_start(struct rkisp2_stats *stats)
+{
+ if (stats->imsc != stats->icr && stats->cur_buf != NULL) {
+ WARN_ONCE(1, "unhandled stats irq: expected %x got %x\n",
+ stats->imsc, stats->icr);
+ stats->rkisp2->debug.stats_irq_delay++;
+
+ /*
+ * If we are in this block then it means the stats buffer has
+ * not been completed in rkisp2_stats_send_measurement_3a, so
+ * complete it here.
+ */
+ spin_lock(&stats->lock);
+ rkisp2_stats_complete_buf(stats, stats->cur_buf);
+ spin_unlock(&stats->lock);
+ }
+
+ /* TODO do we need locking for these two fields? */
+ stats->imsc = rkisp2_read(stats->rkisp2, ISP_ISP3A_IMSC);
+ stats->icr = 0;
+}
+
+/* TODO maybe we can just return void */
+irqreturn_t rkisp2_stats_isr_3a(struct rkisp2_stats *stats)
+{
+ struct rkisp2_device *rkisp2 = stats->rkisp2;
+ u32 status;
+
+ status = rkisp2_read(rkisp2, ISP_ISP3A_MIS);
+ if (!status)
+ return IRQ_NONE;
+
+ spin_lock(&stats->lock);
+
+ rkisp2->debug.stats3a_irq++;
+
+ if (status & stats->imsc)
+ rkisp2_stats_send_measurement_3a(stats, status);
+
+ spin_unlock(&stats->lock);
+
+ return IRQ_HANDLED;
+}
+
+static void rkisp2_init_stats(struct rkisp2_stats *stats)
+{
+ stats->vdev_fmt.fmt.meta.dataformat =
+ V4L2_META_FMT_RKISP2_STATS;
+ stats->vdev_fmt.fmt.meta.buffersize =
+ sizeof(struct rkisp2_stats_buffer);
+}
+
+int rkisp2_stats_register(struct rkisp2_device *rkisp2)
+{
+ struct rkisp2_stats *stats = &rkisp2->stats;
+ struct rkisp2_vdev_node *node = &stats->vnode;
+ struct video_device *vdev = &node->vdev;
+ int ret;
+
+ stats->rkisp2 = rkisp2;
+ mutex_init(&node->vlock);
+ INIT_LIST_HEAD(&stats->stat);
+ spin_lock_init(&stats->lock);
+
+ strscpy(vdev->name, RKISP2_STATS_DEV_NAME, sizeof(vdev->name));
+
+ video_set_drvdata(vdev, stats);
+ vdev->ioctl_ops = &rkisp2_stats_ioctl;
+ vdev->fops = &rkisp2_stats_fops;
+ vdev->release = video_device_release_empty;
+ vdev->lock = &node->vlock;
+ vdev->v4l2_dev = &rkisp2->v4l2_dev;
+ vdev->queue = &node->buf_queue;
+ vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING;
+ vdev->vfl_dir = VFL_DIR_RX;
+ rkisp2_stats_init_vb2_queue(vdev->queue, stats);
+ rkisp2_init_stats(stats);
+ video_set_drvdata(vdev, stats);
+
+ node->pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
+ if (ret)
+ goto error;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(&vdev->dev,
+ "failed to register %s, ret=%d\n", vdev->name, ret);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ media_entity_cleanup(&vdev->entity);
+ mutex_destroy(&node->vlock);
+ stats->rkisp2 = NULL;
+ return ret;
+}
+
+void rkisp2_stats_unregister(struct rkisp2_device *rkisp2)
+{
+ struct rkisp2_stats *stats = &rkisp2->stats;
+ struct rkisp2_vdev_node *node = &stats->vnode;
+ struct video_device *vdev = &node->vdev;
+
+ if (!stats->rkisp2)
+ return;
+
+ vb2_video_unregister_device(vdev);
+ media_entity_cleanup(&vdev->entity);
+ mutex_destroy(&node->vlock);
+}
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 470e5b84ed15..1b5776487991 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1468,6 +1468,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_META_FMT_RK_ISP1_STAT_3A: descr = "Rockchip ISP1 3A Statistics"; break;
case V4L2_META_FMT_RK_ISP1_EXT_PARAMS: descr = "Rockchip ISP1 Ext 3A Params"; break;
case V4L2_META_FMT_RKISP2_PARAMS: descr = "Rockchip ISP2 3A Parameters"; break;
+ case V4L2_META_FMT_RKISP2_STATS: descr = "Rockchip ISP2 3A Statistics"; break;
case V4L2_META_FMT_C3ISP_PARAMS: descr = "Amlogic C3 ISP Parameters"; break;
case V4L2_META_FMT_C3ISP_STATS: descr = "Amlogic C3 ISP Statistics"; break;
case V4L2_META_FMT_MALI_C55_PARAMS: descr = "ARM Mali-C55 ISP Parameters"; break;
diff --git a/include/uapi/linux/rkisp2-config.h b/include/uapi/linux/rkisp2-config.h
index 0e42dbf2c9f2..a45fe79a7716 100644
--- a/include/uapi/linux/rkisp2-config.h
+++ b/include/uapi/linux/rkisp2-config.h
@@ -20,6 +20,17 @@
#define RKISP2_ISP_LSC_SAMPLES_MAX 17
#define RKISP2_ISP_LSC_SECTORS_TBL_SIZE_MAX 16
+#define RKISP2_ISP_AE_MEAN_MAX_LITE 25
+#define RKISP2_ISP_AE_MEAN_MAX_BIG 225
+
+#define RKISP2_ISP_HIST_WEIGHT_GRIDS_SIZE_LITE 25
+#define RKISP2_ISP_HIST_WEIGHT_GRIDS_SIZE_BIG 225
+#define RKISP2_ISP_HIST_WEIGHT_GRIDS_SIZE_MAX RKISP2_ISP_HIST_WEIGHT_GRIDS_SIZE_BIG
+
+#define RKISP2_ISP_HIST_BIN_N_MAX 256
+
+#define RKISP2_ISP_AWB_COUNTS_SIZE 225
+
/**
* enum rkisp2_isp_version - ISP variants
*
@@ -55,6 +66,98 @@ enum rkisp2_isp_set_active_table_when {
RKISP2_ISP_LSC_SET_ACTIVE_TABLE_BEFORE,
};
+enum rkisp2_isp_histogram_mode {
+ RKISP2_ISP_HISTOGRAM_MODE_DISABLE,
+ RKISP2_ISP_HISTOGRAM_MODE_R_HISTOGRAM = 2,
+ RKISP2_ISP_HISTOGRAM_MODE_G_HISTOGRAM,
+ RKISP2_ISP_HISTOGRAM_MODE_B_HISTOGRAM,
+ RKISP2_ISP_HISTOGRAM_MODE_Y_HISTOGRAM
+};
+
+/*
+ * This selects which bits are used from the input data to compute the
+ * histogram
+ */
+enum rkisp2_isp_histogram_data_sel {
+ RKISP2_ISP_HISTOGRAM_DATA_SEL_11_4,
+ RKISP2_ISP_HISTOGRAM_DATA_SEL_10_3,
+ RKISP2_ISP_HISTOGRAM_DATA_SEL_9_2,
+ RKISP2_ISP_HISTOGRAM_DATA_SEL_8_1,
+ RKISP2_ISP_HISTOGRAM_DATA_SEL_7_0,
+};
+
+/*---------- Statistics ------------*/
+
+/**
+ * struct rkisp2_isp_ae_lite - statistics auto exposure data
+ *
+ * @exp_mean_r: Mean luminance value of block xy for r channel
+ * @exp_mean_g: Mean luminance value of block xy for g channel
+ * @exp_mean_b: Mean luminance value of block xy for b channel
+ * @done: This set to nonzero when the stats are ready
+ *
+ * Image is divided into 5x5 blocks on lite and 15x15 blocks on big.
+ */
+struct rkisp2_isp_ae_lite {
+ __u16 exp_mean_r[RKISP2_ISP_AE_MEAN_MAX_LITE];
+ __u16 exp_mean_g[RKISP2_ISP_AE_MEAN_MAX_LITE];
+ __u16 exp_mean_b[RKISP2_ISP_AE_MEAN_MAX_LITE];
+ __u8 done;
+};
+
+/**
+ * struct rkisp2_cif_isp_hist_stat - statistics histogram data
+ *
+ * @hist_bins: measured bin counters. Each bin is a 28 bits unsigned fixed point
+ * type. Bits 0-4 are the fractional part and bits 5-27 are the
+ * integer part.
+ * @done: This set to nonzero when the stats are ready
+ *
+ * There are 256 bins, at least on 3.x.
+ */
+struct rkisp2_isp_hist {
+ __u32 hist_bins[RKISP2_ISP_HIST_BIN_N_MAX];
+ __u8 done;
+};
+
+/**
+ * struct rkisp2_isp_awb - statistics auto white balance data
+ *
+ * @counts_r: Counts of red (18-bits)
+ * @counts_g: Counts of green (18-bits)
+ * @counts_b: Counts of blue (18-bits)
+ * @counts_w: Counts of white point (10-bits)
+ * @done: This set to nonzero when the stats are ready
+ *
+ * TODO Figure out what is being counted
+ */
+struct rkisp2_isp_awb {
+ __u32 counts_r[RKISP2_ISP_AWB_COUNTS_SIZE];
+ __u32 counts_g[RKISP2_ISP_AWB_COUNTS_SIZE];
+ __u32 counts_b[RKISP2_ISP_AWB_COUNTS_SIZE];
+ __u16 counts_w[RKISP2_ISP_AWB_COUNTS_SIZE];
+ __u8 done;
+};
+
+/**
+ * struct rkisp2_stats_buffer - 3A statistics for the RkISP2
+ *
+ * @ae_lite: ae lite stats
+ * @hist_lite: histogram lite stats
+ * @hist_big0: histogram big0 stats
+ * @hist_big1: histogram big0 stats
+ * @hist_big2: histogram big0 stats
+ * @awb: awb stats
+ */
+struct rkisp2_stats_buffer {
+ struct rkisp2_isp_ae_lite ae_lite;
+ struct rkisp2_isp_hist hist_lite;
+ struct rkisp2_isp_hist hist_big0;
+ struct rkisp2_isp_hist hist_big1;
+ struct rkisp2_isp_hist hist_big2;
+ struct rkisp2_isp_awb awb;
+};
+
/*---------- Parameters ------------*/
/**
@@ -66,6 +169,12 @@ enum rkisp2_isp_set_active_table_when {
* @RKISP2_PARAMS_BLOCK_CCM: Color correction matrix (in the CCM block)
* @RKISP2_PARAMS_BLOCK_GOC: Gamma out correction
* @RKISP2_PARAMS_BLOCK_LSC: Lens shading correction
+ * @RKISP2_PARAMS_BLOCK_AE_LITE: AE measurement config (lite)
+ * @RKISP2_PARAMS_BLOCK_HIST_LITE: Histogram measurement config (lite)
+ * @RKISP2_PARAMS_BLOCK_HIST_BIG0: Histogram measurement config (zeroth big block)
+ * @RKISP2_PARAMS_BLOCK_HIST_BIG1: Histogram measurement config (first big block)
+ * @RKISP2_PARAMS_BLOCK_HIST_BIG2: Histogram measurement config (second big block)
+ * @RKISP2_PARAMS_BLOCK_AWB_MEAS: AWB measurements config
* */
enum rkisp2_params_block_type {
RKISP2_PARAMS_BLOCK_BLS,
@@ -74,6 +183,12 @@ enum rkisp2_params_block_type {
RKISP2_PARAMS_BLOCK_CCM,
RKISP2_PARAMS_BLOCK_GOC,
RKISP2_PARAMS_BLOCK_LSC,
+ RKISP2_PARAMS_BLOCK_AE_LITE,
+ RKISP2_PARAMS_BLOCK_HIST_LITE,
+ RKISP2_PARAMS_BLOCK_HIST_BIG0,
+ RKISP2_PARAMS_BLOCK_HIST_BIG1,
+ RKISP2_PARAMS_BLOCK_HIST_BIG2,
+ RKISP2_PARAMS_BLOCK_AWB_MEAS,
};
/**
@@ -134,6 +249,36 @@ struct rkisp2_isp_awb_gains {
__u16 gb;
};
+/**
+ * struct rkisp2_isp_color_cc - Color coefficients
+ *
+ * @r: Red coefficient
+ * @g: Green coefficient
+ * @b: Blue coefficient
+ */
+struct rkisp2_isp_color_cc {
+ __u8 r;
+ __u8 g;
+ __u8 b;
+};
+
+/**
+ * struct rkisp2_isp_awb_color_quad - Group of RGB and luminance for AWB
+ *
+ * TODO redesign this?
+ *
+ * @r: Red
+ * @g: Green
+ * @b: Blue
+ * @y: Y (luminance)
+ */
+struct rkisp2_isp_awb_color_quad {
+ __u8 r;
+ __u8 g;
+ __u8 b;
+ __u8 y;
+};
+
/**
* struct rkisp2_params_bls - RkISP2 params BLS config
*
@@ -323,12 +468,98 @@ struct rkisp2_params_lsc {
__u8 window_mode;
};
+/**
+ * struct rkisp2_params_ae_lite - RKISP2 params AE lite config
+ *
+ * RkISP2 parameters auto exposure measurement configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_AE_LITE`.
+ *
+ * TODO change window_num to enum?
+ *
+ * @header: The RkISP2 parameters block header
+ * @window_num: 0 for 1x1, 1 for 5x5
+ * @meas_window: Size of measurement window. First window for 5x5.
+ */
+struct rkisp2_params_ae_lite {
+ struct v4l2_isp_params_block_header header;
+ __u8 window_num;
+ struct rkisp2_isp_window meas_window;
+} __attribute__((aligned(8)));
+
+/**
+ * struct rkisp2_params_hist_lite RKISP2 params histogram lite config
+ *
+ * RkISP2 parameters histogram configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_HIST_LITE`.
+ *
+ * @header: The RkISP2 parameters block header
+ * @data_sel: Data selection mode (from enum rkisp2_isp_histogram_data_sel)
+ * @mode: Histogram mode (from enum rkisp2_isp_histogram_mode)
+ * @stepsize: Predivider (count every <stepsize> pixel)
+ * @waterline: Waterline for region statics
+ * @coeffs: Coefficients for raw2y formula
+ * @meas_window: Size of first measurement subwindow
+ * @weights: Weights
+ */
+struct rkisp2_params_hist_lite {
+ struct v4l2_isp_params_block_header header;
+ __u8 data_sel;
+ __u8 mode;
+ __u8 stepsize;
+ __u16 waterline;
+ struct rkisp2_isp_color_cc coeffs;
+ struct rkisp2_isp_window meas_window;
+ __u8 weights[RKISP2_ISP_HIST_WEIGHT_GRIDS_SIZE_LITE];
+} __attribute__((aligned(8)));
+
+/**
+ * Same as struct rkisp2_params_hist_lite but for big channel
+ *
+ * RkISP2 parameters histogram configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_HIST_BIG{0,1,2}`.
+ *
+ * @window_num: 0 or 1 for 5x5, 2 or 3 for 15x15
+ */
+struct rkisp2_params_hist_big {
+ struct v4l2_isp_params_block_header header;
+ __u8 window_num;
+ __u8 data_sel;
+ __u8 mode;
+ __u8 stepsize;
+ __u16 waterline;
+ struct rkisp2_isp_color_cc coeffs;
+ struct rkisp2_isp_window meas_window;
+ __u8 weights[RKISP2_ISP_HIST_WEIGHT_GRIDS_SIZE_BIG];
+} __attribute__((aligned(8)));
+
+/**
+ * struct rkisp2_params_awb_meas - Configuration used by rawawb
+ *
+ * RkISP2 parameters AWB measurement configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_AWB_MEAS`.
+ *
+ * @header: The RkISP2 parameters block header
+ * @meas_window: Size of first measurement subwindow (13 bits)
+ * @limits: Limits for white point detection [min, max] (8 bits)
+ * @weights: Weights (6-bits)
+ */
+struct rkisp2_params_awb_meas {
+ struct v4l2_isp_params_block_header header;
+ struct rkisp2_isp_window meas_window;
+ struct rkisp2_isp_awb_color_quad limits[2];
+ __u8 weights[RKISP2_ISP_AWB_COUNTS_SIZE];
+};
+
#define RKISP2_PARAMS_MAX_SIZE \
(sizeof(struct rkisp2_params_bls) +\
sizeof(struct rkisp2_params_awb_gains) +\
sizeof(struct rkisp2_params_csm) +\
sizeof(struct rkisp2_params_ccm) +\
sizeof(struct rkisp2_params_goc) +\
- sizeof(struct rkisp2_params_lsc))
+ sizeof(struct rkisp2_params_lsc) +\
+ sizeof(struct rkisp2_params_ae_lite) +\
+ sizeof(struct rkisp2_params_hist_lite) +\
+ sizeof(struct rkisp2_params_hist_big) * 3 +\
+ sizeof(struct rkisp2_params_awb_meas))
#endif /* _UAPI_RKISP2_CONFIG_H */
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 1d418a752d89..399f4d73af15 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -878,6 +878,7 @@ struct v4l2_pix_format {
/* Vendor specific - used for RKISP2 camera sub-system */
#define V4L2_META_FMT_RKISP2_PARAMS v4l2_fourcc('R', 'K', '2', 'P') /* Rockchip ISP2 Parameters */
+#define V4L2_META_FMT_RKISP2_STATS v4l2_fourcc('R', 'K', '2', 'S') /* Rockchip ISP2 3A Statistics */
/* Vendor specific - used for C3_ISP */
#define V4L2_META_FMT_C3ISP_PARAMS v4l2_fourcc('C', '3', 'P', 'M') /* Amlogic C3 ISP Parameters */
--
2.47.2
^ permalink raw reply related
* Re: [RFC PATCH 1/5] media: dt-bindings: Add rockchip rkisp2 bindings
From: Paul Elder @ 2026-04-24 18:05 UTC (permalink / raw)
To: laurent.pinchart
Cc: michael.riesch, xuhf, stefan.klug, linux-media, linux-arm-kernel,
linux-rockchip, linux-kernel, Heiko Stuebner
In-Reply-To: <20260424175853.638202-2-paul.elder@ideasonboard.com>
Hi me,
Quoting Paul Elder (2026-04-25 02:58:46)
> Add documentation for the Rockchip rkisp2 bindings. This is meant to
> support multiple versions of Rockchip ISPs going forward, including the
> 2.x series and 3.x series. The current version only adds the compatible
> for the RK3588, which is a 3.0.
>
> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
> ---
> .../bindings/media/rockchip-isp2.yaml | 127 ++++++++++++++++++
> 1 file changed, 127 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/rockchip-isp2.yaml
>
> diff --git a/Documentation/devicetree/bindings/media/rockchip-isp2.yaml b/Documentation/devicetree/bindings/media/rockchip-isp2.yaml
> new file mode 100644
> index 000000000000..f4ef089adf6e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/rockchip-isp2.yaml
> @@ -0,0 +1,127 @@
> +# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/media/rockchip-isp2.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Rockchip SoC Image Signal Processing unit v2
> +
> +maintainers:
> + - Paul Elder <paul.elder@ideasonboard.com>
> +
> +description: |
> + Rockchip ISP2 is the Camera interface for the Rockchip series of SoCs which
> + contains image processing, scaling, and compression functions.
> +
> +properties:
> + compatible:
> + enum:
> + - rockchip,rk3588-isp
> +
> + reg:
> + maxItems: 1
> +
> + interrupts:
> + minItems: 3
> + maxItems: 3
These should be 2.
> +
> + interrupt-names:
> + items:
> + - const: isp_irq
> + - const: mi_irq
> +
> + clocks:
> + minItems: 3
> + items:
> + - description: ISP AXI clock (aclk)
> + - description: ISP AHB clock (hclk)
> + - description: ISP core clock (isp)
> + # for rk3588
> + - description: ISP core clock (marvin)
> + - description: ISP core clock (vicap)
> +
> + clock-names:
> + minItems: 3
> + items:
> + - const: aclk
> + - const: hclk
> + - const: clk_core
> + # for rk3588
> + - const: clk_core_marvin
> + - const: clk_core_vicap
> +
> + iommus:
> + maxItems: 1
> +
> + power-domains:
> + minItems: 1
This can be removed.
> + items:
> + - description: ISP power domain
> +
> + ports:
> + $ref: /schemas/graph.yaml#/properties/ports
> +
> + properties:
> + port@0:
> + $ref: /schemas/graph.yaml#/$defs/port-base
> + unevaluatedProperties: false
> + description: connection point for VICAP in inline mode
> +
> + properties:
> + endpoint:
> + $ref: video-interfaces.yaml#
> + unevaluatedProperties: false
> +
> + required:
> + - port@0
> +
> +required:
> + - compatible
> + - reg
> + - interrupts
> + - clocks
> + - clock-names
> + - power-domains
> +
> +allOf:
> + - if:
> + properties:
> + compatible:
> + contains:
> + const: rockchip,rk3588-isp
> + then:
> + properties:
> + clocks:
> + minItems: 5
> + clock-names:
> + minItems: 5
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> +
> + #include <dt-bindings/clock/rockchip,rk3588-cru.h>
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> + #include <dt-bindings/power/rk3588-power.h>
> +
> + parent0: parent {
> + #address-cells = <2>;
> + #size-cells = <2>;
> +
> + isp0: isp@fdcb0000 {
> + compatible = "rockchip,rk3588-isp";
> + reg = <0x0 0xfdcb0000 0x0 0x7f00>;
> + interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH 0>,
> + <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH 0>,
> + <GIC_SPI 134 IRQ_TYPE_LEVEL_HIGH 0>;
> + interrupt-names = "isp_irq", "mi_irq";
I removed the interrupt name and forgot to remove the interrupt.
I think the same problem is present in the dt node in the next patch.
Paul
> + clocks = <&cru ACLK_ISP0>, <&cru HCLK_ISP0>,
> + <&cru CLK_ISP0_CORE>, <&cru CLK_ISP0_CORE_MARVIN>,
> + <&cru CLK_ISP0_CORE_VICAP>;
> + clock-names = "aclk", "hclk", "clk_core",
> + "clk_core_marvin", "clk_core_vicap";
> + power-domains = <&power RK3588_PD_VI>;
> + iommus = <&isp0_mmu>;
> + };
> + };
> --
> 2.47.2
>
^ permalink raw reply
* Re: [PATCH v11 00/14] barrier: Add smp_cond_load_{relaxed,acquire}_timeout()
From: Ankur Arora @ 2026-04-24 18:10 UTC (permalink / raw)
To: Andrew Morton
Cc: Ankur Arora, linux-kernel, linux-arch, linux-arm-kernel, linux-pm,
bpf, arnd, catalin.marinas, will, peterz, mark.rutland, harisokn,
cl, ast, rafael, daniel.lezcano, memxor, zhenglifeng1, xueshuai,
rdunlap, david.laight.linux, joao.m.martins, boris.ostrovsky,
konrad.wilk, ashok.bhat
In-Reply-To: <20260424072845.5eba94c2d732204f143b8629@linux-foundation.org>
Andrew Morton <akpm@linux-foundation.org> writes:
> On Wed, 8 Apr 2026 17:55:24 +0530 Ankur Arora <ankur.a.arora@oracle.com> wrote:
>
>> The core kernel often uses smp_cond_load_{relaxed,acquire}() to spin
>> on condition variables with architectural primitives used to avoid
>> hammering the relevant cachelines.
>>
>> ...
>>
>> Accordingly add two interfaces (with their generic and arm64 specific
>> implementations):
>>
>> smp_cond_load_relaxed_timeout(ptr, cond_expr, time_expr, timeout)
>> smp_cond_load_acquire_timeout(ptr, cond_expr, time_expr, timeout)
>>
>> Also add tif_need_resched_relaxed_wait() which wraps the polling
>> pattern and its scheduler specific details in poll_idle().
>> In addition add atomic_cond_read_*_timeout(),
>> atomic64_cond_read_*_timeout(), and atomic_long wrappers.
>
> AI review has been busy:
> https://sashiko.dev/#/patchset/20260408122538.3610871-1-ankur.a.arora@oracle.com
Quite busy. Thanks for flagging them.
From a quick look most seemed largely benign but let me go through them
in more detail.
--
ankur
^ permalink raw reply
* Re: [PATCH 15/15] arm64: dts: ti: beagley-ai: Enable HDMI display and audio
From: Robert Nelson @ 2026-04-24 18:26 UTC (permalink / raw)
To: Tomi Valkeinen, Andrei Aldea
Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Lee Jones, Aradhya Bhatia, Nishanth Menon, Vignesh Raghavendra,
Swamil Jain, Devarsh Thakkar, Louis Chauvet, devicetree,
dri-devel, linux-kernel, linux-arm-kernel, Andrew Davis
In-Reply-To: <cb19eafe-6c25-4a2a-bd31-9fb280837623@ideasonboard.com>
On Fri, Apr 24, 2026 at 12:04 PM Tomi Valkeinen
<tomi.valkeinen@ideasonboard.com> wrote:
>
> Hi,
>
> On 24/04/2026 19:16, Robert Nelson wrote:
> > On Mon, Apr 20, 2026 at 8:04 AM Tomi Valkeinen
> > <tomi.valkeinen@ideasonboard.com> wrote:
> >>
> >> From: Andrew Davis <afd@ti.com>
> >>
> >> Enable HDMI support for BeagleY-AI platform. The display controller used is
> >> TIDSS and the HDMI bridge used is IT66122.
> >>
> >> Based on DT by: Robert Nelson <robertcnelson@gmail.com>
> >> Signed-off-by: Andrew Davis <afd@ti.com>
> >> Signed-off-by: Swamil Jain <s-jain1@ti.com>
> >> [tomi.valkeinen: cosmetic fixes]
> >> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
> >
> > Tested-by: Robert Nelson <robertcnelson@gmail.com>
> >
> > Thank you for getting the display back end working on j722s family!
> What displays did you test? I don't have OLDI panel...
I was buggin @Andrei Aldea to get you one. I also don't have the
'special' Lincoln tech display or the TI cloned panel that works on
OLDI.. Right now just HDMI as i decided to tackle cc33xx wifi again
today on mainline..
Regards,
--
Robert Nelson
https://rcn-ee.com/
^ permalink raw reply
* Re: [PATCH] cpu/hotplug: Fix NULL kobject warning in cpuhp_smt_enable()
From: Jonathan Cameron @ 2026-04-24 18:27 UTC (permalink / raw)
To: Catalin Marinas
Cc: Jinjie Ruan, Thomas Gleixner, peterz, sudeep.holla, yangyicong,
dietmar.eggemann, linux-kernel, James Morse, linux-arm-kernel
In-Reply-To: <aetnTT51ucm2azGG@arm.com>
On Fri, 24 Apr 2026 13:51:25 +0100
Catalin Marinas <catalin.marinas@arm.com> wrote:
> (updating Jonathan's email address to match MAINTAINERS)
Thanks!
>
> On Fri, Apr 24, 2026 at 09:56:24AM +0800, Jinjie Ruan wrote:
> > On 4/24/2026 4:11 AM, Catalin Marinas wrote:
> > > On Thu, Apr 23, 2026 at 08:32:34PM +0800, Jinjie Ruan wrote:
> > >> On 4/23/2026 6:08 PM, Thomas Gleixner wrote:
> > >>> On Sat, Apr 18 2026 at 12:55, Catalin Marinas wrote:
> > >>>> Another option would have been to avoid marking such CPUs present but I
> > >>>> think this will break other things. Yet another option is to register
> > >>>> all CPU devices even if they never come up (like maxcpus greater than
> > >>>> actual CPUs).
> > >>>>
> > >>>> Opinions? It might be an arm64+ACPI-only thing.
> > >>>
> > >>> I think so. The proper thing to do is to apply sane limits:
> > >>>
> > >>> 1) The possible CPUs enumerated by firmware N_POSSIBLE_FW
> > >>>
> > >>> 2) The maxcpus limit on the command line N_MAXCPUS_CL
> > >>>
> > >>> So the actual possible CPUs evaluates to:
> > >>>
> > >>> num_possible = min(N_POSSIBLE_FW, N_MAXCPUS_CL, CONFIG_NR_CPUS);
> > >>>
> > >>> The evaluation of the firmware should not mark CPUs present which are
> > >>> actually not. ACPI gives you that information. See:
> > >>>
> > >>> 5.2.12.14 GIC CPU Interface (GICC) Structure
> > >>>
> > >>> in the ACPI spec. That has two related bits:
> > >>>
> > >>> Enabled:
> > >>>
> > >>> If this bit is set, the processor is ready for use. If this bit is
> > >>> clear and the Online Capable bit is set, the system supports enabling
> > >>> this processor during OS runtime. If this bit is clear and the Online
> > >>> Capable bit is also clear, this processor is un- usable, and the
> > >>> operating system support will not attempt to use it.
> > >>>
> > >>> Online Capable:
> > >>>
> > >>> The information conveyed by this bit depends on the value of the
> > >>> Enabled bit. If the Enabled bit is set, this bit is reserved and must
> > >>> be zero. Otherwise, if this bit is set, the system supports enabling
> > >>> this processor later during OS runtime
> > >>>
> > >>> So the combination of those gives you the right answer:
> > >>>
> > >>> Enabled Online
> > >>> Capable
> > >>> 0 0 Not present, not possible
> > >>> 0 1 Not present, but possible to "hotplug" layter
> > >>> 1 0 Present
> > >>> 1 1 Invalid
> > >>
> > >> On x86, it seems that all CPUs with the ACPI_MADT_ENABLED bit set will
> > >> be marked as present.
> > >>
> > >> acpi_parse_x2apic()
> > >> -> enabled = processor->lapic_flags & ACPI_MADT_ENABLED
> > >> -> topology_register_apic(enabled)
> > >> -> topo_register_apic(enabled)
> > >> -> set_cpu_present(cpu, true)
> > >
> > > Yes but arm64 marks all CPUs present even if !ACPI_MADT_ENABLED as we
> > > don't have the notion of hardware CPU hotplug.
> > >
> > > I need to dig some more into the original vCPU hotplug support and why
> > > we ended up with all CPUs marked as present even if not calling
> > > register_cpu():
James may remember more but I think there were some usecases
which broken if we reported CPUs that might turn up later as
not present. We spent some time discussing what to do about this
on various calls, leading to just giving up and marking them present
always.
I think it was something to do with userspace code that would allocate
stuff based on the sysfs present information and die horribly when more
CPUs turned up later.
I'm not sure I ever came across that code. Maybe it was something
James had come across. I may be completely wrong but my memory
is hinting that it was something Android related?
> > >
> > > https://lore.kernel.org/linux-arm-kernel/20240529133446.28446-1-Jonathan.Cameron@huawei.com/
> > >
> > > What's the MADT GICC provided by qemu with "-smp cpus=4,maxcpus=8"? If
> > > it says Enabled for the first 4 and Online Capable for the rest, maybe
> > > we can try something like below:
> >
> > Yes, you are absolutely right,Enabled for the first 4(with GIC Flags:
> > 0x1, bit0 set) and Online Capable for the rest(with GIC Flags: 0x8, bit3
> > set). The ACPI MADT disassembly result is as follows:
>
> That's great, thanks for checking.
>
> I'd like to get some feedback from Jonathan and James as they
> contributed the vCPU hotplug support. The reason was for kubernetes to
> add vCPUs to an existing VM. Hopefully no-one relied on
> /sys/devices/system/cpu/present to report 0-7 in the above
> configuration.
I think not in the kubernetes case as such, but in general userspace
code which might run on the same systems :(
J
>
> Have you tried the vCPU hotplug with this patch (the original use-case)?
>
> Anyway, feel free to post a v2 with the above proposal, cc Jonathan (on
> kernel.org) and James Morse and we'll take it from there. You can add a
> suggested-by me.
>
> Thanks.
>
^ permalink raw reply
* Re: [PATCH rc v2 1/5] iommu/arm-smmu-v3: Add arm_smmu_adopt_strtab() for kdump
From: Nicolin Chen @ 2026-04-24 18:33 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: will, robin.murphy, kevin.tian, joro, praan, baolu.lu,
miko.lenczewski, smostafa, linux-arm-kernel, iommu, linux-kernel,
stable, jamien
In-Reply-To: <20260424165613.GC3444440@nvidia.com>
On Fri, Apr 24, 2026 at 01:56:13PM -0300, Jason Gunthorpe wrote:
> On Wed, Apr 15, 2026 at 02:17:36PM -0700, Nicolin Chen wrote:
> > +static int arm_smmu_adopt_strtab_2lvl(struct arm_smmu_device *smmu, u32 cfg_reg,
[..]
> > + cfg->l2.l1tab = devm_memremap(
> > + smmu->dev, dma, num_l1_ents * sizeof(struct arm_smmu_strtab_l1),
> > + MEMREMAP_WB);
>
> WB shouldn't be unconditional? If the SMMU is working non-coherently
> we need to map it NC. Same remark everwhere
Hmm, I am trying to add a coherent-only gate for the series.
MEMREMAP_WC might work. But we cannot verify that on a coherent
SMMU, right?
Nicolin
^ permalink raw reply
* Re: [PATCH rc v2 2/5] iommu/arm-smmu-v3: Implement is_attach_deferred() for kdump
From: Nicolin Chen @ 2026-04-24 18:44 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: will, robin.murphy, kevin.tian, joro, praan, baolu.lu,
miko.lenczewski, smostafa, linux-arm-kernel, iommu, linux-kernel,
stable, jamien
In-Reply-To: <20260424165927.GD3444440@nvidia.com>
On Fri, Apr 24, 2026 at 01:59:27PM -0300, Jason Gunthorpe wrote:
> On Wed, Apr 15, 2026 at 02:17:37PM -0700, Nicolin Chen wrote:
> > +static bool arm_smmu_is_attach_deferred(struct device *dev)
> > +{
> > + struct arm_smmu_master *master = dev_iommu_priv_get(dev);
> > + struct arm_smmu_device *smmu = master->smmu;
> > + int i;
> > +
> > + if (!(smmu->options & ARM_SMMU_OPT_KDUMP))
> > + return false;
> > +
> > + for (i = 0; i < master->num_streams; i++) {
> > + u32 sid = master->streams[i].id;
> > + struct arm_smmu_ste *step;
> > +
> > + /* Guard against unpopulated L2 entries in the adopted table */
> > + if ((smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) &&
> > + !smmu->strtab_cfg.l2.l2ptrs[arm_smmu_strtab_l1_idx(sid)])
> > + continue;
>
> This can probably just call arm_smmu_init_sid_strtab()
>
> I think it is OK to allocate another level 2 here and it also has
> protections for SID out of range..
Actually, sashiko pointed out that this guard is a dead code.
arm_smmu_init_sid_strtab() is called in arm_smmu_insert_master().
Thanks
Nicolin
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox