hwlat improvements and osnoise tracer
Subsystems (4)
cover hwlat improvements and osnoise tracer
This series proposes a set of improvements and new features for the tracing subsystem to facilitate the debugging of low latency deployments.
Currently, hwlat runs on a single CPU at a time, migrating across a set of CPUs in a round-robin fashion. The first three patches are changes made to allow hwlat to run on multiple CPUs in parallel, increasing the chances of detecting a hardware latency.
The fourth patch is a helper to print a timestamp in a u64 in seconds.nanoseconds format on tracepoints.
The fifth patch proposes a new tracer named osnoise and aims to help users of isolcpus= (or a similar method) to measure how much noise the OS and the hardware add to the isolated application. The osnoise tracer bases on the hwlat detector code. The difference is that, instead of sampling with interrupts disabled, the osnoise tracer samples the CPU with interrupts and preemption enabled. In this way, the sampling thread will suffer any source of noise from the OS. The detection and classification of the type of noise are then made by observing the entry points of NMIs, IRQs, SoftIRQs, and threads. If none of these sources of noise is detected, the tool associates the noise with the hardware. The tool periodically prints a status, printing the total noise of the period, the max single noise observed, the percentage of CPU available for the task, along with the counters of each source of the noise. To debug the sources of noise, the tracer also adds a set of tracepoints that print any NMI, IRQ, SofIRQ, and thread occurrence. These tracepoints print the starting time and the noise's net duration at the end of the noise. In this way, it reduces the number of tracepoints (one instead of two) and the need to manually accounting the contribution of each noise independently.
Daniel Bristot de Oliveira (4):
tracing/hwlat: Add a cpus file specific for hwlat_detector tracing/hwlat: Implement the mode config option tracing/hwlat: Implement the per-cpu mode
tracing: Add the osnoise tracer
Steven Rostedt (1):
tracing: Add __print_ns_to_secs() and __print_ns_without_secs()
helpers
Documentation/trace/hwlat_detector.rst | 29 +- Documentation/trace/osnoise_tracer.rst | 149 ++ include/linux/ftrace_irq.h | 16 + include/trace/events/osnoise.h | 141 ++ include/trace/trace_events.h | 25 + kernel/trace/Kconfig | 34 + kernel/trace/Makefile | 1 + kernel/trace/trace.h | 9 +- kernel/trace/trace_entries.h | 27 + kernel/trace/trace_hwlat.c | 445 +++++- kernel/trace/trace_osnoise.c | 1714 ++++++++++++++++++++++++ kernel/trace/trace_output.c | 72 +- 12 files changed, 2604 insertions(+), 58 deletions(-) create mode 100644 Documentation/trace/osnoise_tracer.rst create mode 100644 include/trace/events/osnoise.h create mode 100644 kernel/trace/trace_osnoise.c
Signature · 1 line
2.30.2
RFC PATCH 1/5 tracing/hwlat: Add a cpus file specific for hwlat_detector
Provides a "cpus" interface to the hardware latency detector. By default, it lists all CPUs, allowing hwlatd threads to run on any online CPU of the system.
It serves to restrict the execution of hwlatd to the set of CPUs writing via this interface. Note that hwlatd also respects the "tracing_cpumask." Hence, hwlatd threads will run only on the set of CPUs allowed here AND on "tracing_cpumask."
Why not keep just "tracing_cpumask"? Because the user might be interested in tracing what is running on other CPUs. For instance, one might run hwlatd in one HT CPU while observing what is running on the sibling HT CPU. The cpu list format is also more intuitive.
Also in preparation to the per-cpu mode.
--- Documentation/trace/hwlat_detector.rst | 14 +-- kernel/trace/trace_hwlat.c | 125 ++++++++++++++++++++++++- 2 files changed, 131 insertions(+), 8 deletions(-)
Documentation/trace/hwlat_detector.rst +8 -6
@@ -73,11 +73,13 @@ in /sys/kernel/tracing: - tracing_threshold - minimum latency value to be considered (usecs) - tracing_max_latency - maximum hardware latency actually observed (usecs) - - tracing_cpumask - the CPUs to move the hwlat thread across - - hwlat_detector/width - specified amount of time to spin within window (usecs) - - hwlat_detector/window - amount of time between (width) runs (usecs) + - hwlat_detector/width - specified amount of time to spin within window (usecs) + - hwlat_detector/window - amount of time between (width) runs (usecs) + - hwlat_detector/cpus - the CPUs to move the hwlat thread across The hwlat detector's kernel thread will migrate across each CPU specified in -tracing_cpumask between each window. To limit the migration, either modify -tracing_cpumask, or modify the hwlat kernel thread (named [hwlatd]) CPU -affinity directly, and the migration will stop. +cpus list between each window. The hwlat detector will also obey the +tracing_cpumask, so the thread will migrate on the set of cpus that is +both on its cpus list and in the global tracing_cpumask file. +To limit the migration, either modify cpumask, or modify the hwlat kernel +thread (named [hwlatd]) CPU affinity directly, and the migration will stop.
kernel/trace/trace_hwlat.c +123 -2
173 lines not on this page · raw message
Signature · 1 line
2.30.2
Steven Rostedt
maintainerRe: [RFC PATCH 1/5] tracing/hwlat: Add a cpus file specific for hwlat_detector
Quoted 9 lines · On Thu, 8 Apr 2021 16:13:19 +0200 Daniel Bristot de Oliveira…
Provides a "cpus" interface to the hardware latency detector. By default, it lists all CPUs, allowing hwlatd threads to run on any online CPU of the system.
It serves to restrict the execution of hwlatd to the set of CPUs writing via this interface. Note that hwlatd also respects the "
tracing_cpumask." Hence, hwlatd threads will run only on the set of CPUs allowed here AND on "tracing_cpumask."
Quoted 6 lines
Why not keep just "
tracing_cpumask"? Because the user might be interested in tracing what is running on other CPUs. For instance, one might run hwlatd in one HT CPU while observing what is running on the sibling HT CPU. The cpu list format is also more intuitive.Also in preparation to the per-cpu mode.
OK, I'm still not convinced that you couldn't use tracing_cpumask here. Because we have instances, and tracing_cpumask is defined per instance, you could simply do:
# cd /sys/kernel/tracing # mkdir instances/hwlat # echo a > instances/hwlat/tracing_cpumask # echo hwlat > instances/hwlat/current_tracer
Now the tracing_cpumask above only affects the hwlat tracer.
I'm just reluctant to add more tracing files if the current ones can be used without too much trouble. For being intuitive, let's make user space tools hide the nastiness of the kernel interface ;-)
-- Steve
Re: [RFC PATCH 1/5] tracing/hwlat: Add a cpus file specific for hwlat_detector
Quoted 28 lines · Steven Rostedt
Quoted 9 lines · On Thu, 8 Apr 2021 16:13:19 +0200 Daniel Bristot de Oliveira…
Provides a "cpus" interface to the hardware latency detector. By default, it lists all CPUs, allowing hwlatd threads to run on any online CPU of the system.
It serves to restrict the execution of hwlatd to the set of CPUs writing via this interface. Note that hwlatd also respects the "
tracing_cpumask." Hence, hwlatd threads will run only on the set of CPUs allowed here AND on "tracing_cpumask."Quoted 6 lines
Why not keep just "
tracing_cpumask"? Because the user might be interested in tracing what is running on other CPUs. For instance, one might run hwlatd in one HT CPU while observing what is running on the sibling HT CPU. The cpu list format is also more intuitive.Also in preparation to the per-cpu mode.
OK, I'm still not convinced that you couldn't use
tracing_cpumaskhere. Because we have instances, andtracing_cpumaskis defined per instance, you could simply do:# cd /sys/kernel/tracing # mkdir instances/hwlat # echo a > instances/hwlat/tracing_cpumask # echo hwlat > instances/hwlat/current_tracer
Quoted 5 lines
Now the
tracing_cpumaskabove only affects the hwlat tracer.I'm just reluctant to add more tracing files if the current ones can be used without too much trouble. For being intuitive, let's make user space tools hide the nastiness of the kernel interface ;-)
[discussing about the cpus file in both hwlat and osnoise here...]
I see your point, but by having two different instances give you two different output "trace" files... and it is not that always practical to merge them when using only the tracefs interface (I like to use it, and it is very handy when dealing with immutable systems, on customers...).
Thinking aloud, one might say: sort the two trace files by timestamp...
and other might reply: but some lines do not have a timestamp associated, e.g., the stacktrace.
Anyway, the cpus file on hwlat is not a super essential thing, I agree... interrupts are disabled, so not much could go wrong (although I really needed the trace from a sibling cpu in a real case).
But for the osnoise tracer the cpus file is really useful. For instance, on a system with the CPU 7 isolated:
----- %< -----
# echo 7 > osnoise/cpus
# echo target_cpu == 7 > events/sched/sched_wakeup/filter
# echo stacktrace if target_cpu == 7 > events/sched/sched_wakeup/trigger
# echo 1 > events/sched/sched_wakeup/enable
# echo osnoise:thread_noise > set_event
# echo osnoise > current_tracer
# cat trace
[find...]
kworker/0:1-7 [000] d..5 1820.717780: <stack trace>
=> trace_event_raw_event_sched_wakeup_template
=> __traceiter_sched_wakeup
=> ttwu_do_wakeup
=> try_to_wake_up
=> __queue_work
=> queue_delayed_work_on
=> vmstat_shepherd
=> process_one_work
=> worker_thread
=> kthread
=> ret_from_fork
kworker/7:1-410 [007] d..3 1820.717790: thread_noise: kworker/7:1:410 start 1820.717786519 duration 3626 ns
osnoise/7-1000 [007] .... 1821.582340: 1000000 90 99.99100 15 1 0 12 6 1
----- >% -----It was possible to easily find that the '1' thread noise was a kworker, dispatched from CPU 0, and that it was dispatched by "vmstat_shepherd".
Also, the osnoise dir is not added to a new instance... so, it only costs "one" file...
Quoted 2 lines
-- Steve
Steven Rostedt
maintainerRe: [RFC PATCH 1/5] tracing/hwlat: Add a cpus file specific for hwlat_detector
Quoted 28 lines · On Thu, 15 Apr 2021 15:09:50 +0200 Daniel Bristot de Oliveira…
But for the osnoise tracer the cpus file is really useful. For instance, on a system with the CPU 7 isolated:
----- %< -----
# echo 7 > osnoise/cpus # echo target_cpu == 7 > events/sched/sched_wakeup/filter # echo stacktrace if target_cpu == 7 > events/sched/sched_wakeup/trigger # echo 1 > events/sched/sched_wakeup/enable # echo osnoise:thread_noise > set_event # echo osnoise > current_tracer # cat trace [find...] kworker/0:1-7 [000] d..5 1820.717780: <stack trace> => trace_event_raw_event_sched_wakeup_template => __traceiter_sched_wakeup => ttwu_do_wakeup => try_to_wake_up => __queue_work => queue_delayed_work_on => vmstat_shepherd => process_one_work => worker_thread => kthread => ret_from_fork kworker/7:1-410 [007] d..3 1820.717790: thread_noise: kworker/7:1:410 start 1820.717786519 duration 3626 ns osnoise/7-1000 [007] .... 1821.582340: 1000000 90 99.99100 15 1 0 12 6 1 ----- >% -----
Quoted 5 lines
It was possible to easily find that the '1' thread noise was a kworker, dispatched from CPU 0, and that it was dispatched by "
vmstat_shepherd".Also, the osnoise dir is not added to a new instance... so, it only costs "one" file...
Every file counts. ;-)
What you did not articulate well, is that you want the other trace points to be traced on all CPUs (maybe) when the osnoise threads are on a few (or vice versa).
OK, for osnoise, I can see how it is useful. But as you said above, for hwlat tracer, it's not as useful.
-- Steve
Re: [RFC PATCH 1/5] tracing/hwlat: Add a cpus file specific for hwlat_detector
Quoted 2 lines · Steven Rostedt
OK, for osnoise, I can see how it is useful. But as you said above, for hwlat tracer, it's not as useful.
I agree, it is not as useful.
-- Daniel
RFC PATCH 2/5 tracing/hwlat: Implement the mode config option
In reply to Daniel Bristot de OliveiraProvides the "mode" config to the hardware latency detector. hwlatd has two different operation modes. The default mode is the "round-robin" one, in which a single hwlatd thread runs, migrating among the allowed CPUs in a "round-robin" fashion. This is the current behavior.
The "none" sets the allowed cpumask for a single hwlatd thread at the startup, but skips the round-robin, letting the scheduler handle the migration.
In preparation to the per-cpu mode.
--- Documentation/trace/hwlat_detector.rst | 21 +++- kernel/trace/trace_hwlat.c | 157 +++++++++++++++++++++++-- 2 files changed, 162 insertions(+), 16 deletions(-)
Documentation/trace/hwlat_detector.rst +15 -6
@@ -76,10 +76,19 @@ in /sys/kernel/tracing: - hwlat_detector/width - specified amount of time to spin within window (usecs) - hwlat_detector/window - amount of time between (width) runs (usecs) - hwlat_detector/cpus - the CPUs to move the hwlat thread across + - hwlat_detector/mode - the thread mode + +By default, the hwlat detector's kernel thread will migrate across each CPU +specified in cpumask at the beginning of a new window, in a round-robin +fashion. This behavior can be changed by changing the thread mode, +the available options are: + + - none: do not force migration + - round-robin: migrate across each CPU specified in cpus between each window + +By default, hwlat detector will also obey the tracing_cpumask, so the thread +will be placed only in the set of cpus that is both on the hwlat detector's +cpus and in the global tracing_cpumask file. The user can overwrite the +cpumask by setting it manually. Changing the hwlatd affinity externally, +e.g., via taskset tool, will disable the round-robin migration. -The hwlat detector's kernel thread will migrate across each CPU specified in -cpus list between each window. The hwlat detector will also obey the -tracing_cpumask, so the thread will migrate on the set of cpus that is -both on its cpus list and in the global tracing_cpumask file. -To limit the migration, either modify cpumask, or modify the hwlat kernel -thread (named [hwlatd]) CPU affinity directly, and the migration will stop.
kernel/trace/trace_hwlat.c +147 -10
219 lines not on this page · raw message
Signature · 1 line
2.30.2
kernel test robot
build botRe: [RFC PATCH 2/5] tracing/hwlat: Implement the mode config option
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch]
Hi Daniel,
[FYI, it's a private test report for your RFC patch.]
[auto build test WARNING on tip/perf/core]
[also build test WARNING on trace/for-next linux/master linus/master v5.12-rc6 next-20210408] [If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Daniel-Bristot-de-Oliveira/hwlat-improvements-and-osnoise-tracer/20210408-221655 base: https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git cface0326a6c2ae5c8f47bd466f07624b3e348a7 config: openrisc-randconfig-r013-20210408 (attached as .config) compiler: or1k-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/a09dc6bb34fa0ac2596763b3a0285717799b57ce
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Daniel-Bristot-de-Oliveira/hwlat-improvements-and-osnoise-tracer/20210408-221655 git checkout a09dc6bb34fa0ac2596763b3a0285717799b57ce
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=openrisc
If you fix the issue, kindly add following tag as appropriate
All warnings (new ones prefixed by >>):
kernel/trace/trace_hwlat.c: In function 'hwlat_mode_write':
>> kernel/trace/trace_hwlat.c:711:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable]
711 | int ret;
| ^~~vim +/ret +711 kernel/trace/trace_hwlat.c
691
692 /**
693 * hwlat_mode_write - Write function for "mode" entry
694 * @filp: The active open file structure
695 * @ubuf: The user buffer that contains the value to write
696 * @cnt: The maximum number of bytes to write to "file"
697 * @ppos: The current position in @file
698 *
699 * This function provides a write implementation for the "mode" interface
700 * to the hardware latency detector. hwlatd has different operation modes.
701 * The "none" sets the allowed cpumask for a single hwlatd thread at the
702 * startup and lets the scheduler handle the migration. The default mode is
703 * the "round-robin" one, in which a single hwlatd thread runs, migrating
704 * among the allowed CPUs in a round-robin fashion.
705 */
706 static ssize_t hwlat_mode_write(struct file *filp, const char __user *ubuf,
707 size_t cnt, loff_t *ppos)
708 {
709 const char *mode;
710 char buf[64];
> 711 int ret;
712 int i;
713
714 if (hwlat_busy)
715 return -EBUSY;
716
717 if (cnt >= sizeof(buf))
718 return -EINVAL;
719
720 if (copy_from_user(buf, ubuf, cnt))
721 return -EFAULT;
722
723 buf[cnt] = 0;
724
725 mode = strstrip(buf);
726
727 ret = -EINVAL;
728
729 for (i = 0; i < MODE_MAX; i++) {
730 if (strcmp(mode, thread_mode_str[i]) == 0) {
731 hwlat_data.thread_mode = i;
732 ret = cnt;
733 }
734 }
735
736 *ppos += cnt;
737
738 return cnt;
739 }
740
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]
Steven Rostedt
maintainerRe: [RFC PATCH 2/5] tracing/hwlat: Implement the mode config option
In reply to Daniel Bristot de OliveiraQuoted 14 lines · On Thu, 8 Apr 2021 16:13:20 +0200 Daniel Bristot de Oliveira…
kernel/trace/trace_hwlat.c +14
-0+/** +* hwlat_mode_write - Write function for "mode" entry +* @filp: The active open file structure +* @ubuf: The user buffer that contains the value to write +* @cnt: The maximum number of bytes to write to "file" +* @ppos: The current position in @file +* +* This function provides a write implementation for the "mode" interface +* to the hardware latency detector. hwlatd has different operation modes. +* The "none" sets the allowed cpumask for a single hwlatd thread at the +* startup and lets the scheduler handle the migration. The default mode is +* the "round-robin" one, in which a single hwlatd thread runs, migrating +* among the allowed CPUs in a round-robin fashion. +*/
kernel/trace/trace_hwlat.c hwlat_window_write(struct file *filp, const char __user *ubuf, · line 715
Quoted 10 lines
kernel/trace/trace_hwlat.c +10
-0+static ssize_t hwlat_mode_write (struct file *filp,const char __user *ubuf, +size_t cnt,loff_t *ppos) +{ +const char *mode; +char buf[64 ]; +int ret; +int i; + +if (hwlat_busy) +return -EBUSY ;
So we can't switch modes while running?
Also, with this implemented, you can remove the disable_migrate variable, and just switch the mode to NONE when it's detected that the affinity mask of the thread has been changed.
-- Steve
Quoted 26 lines
kernel/trace/trace_hwlat.c +26
-0+ +if (cnt >=sizeof (buf)) +return -EINVAL ; + +if (copy_from_user (buf, ubuf, cnt)) +return -EFAULT ; + + buf[cnt] =0 ; + + mode =strstrip (buf); + + ret = -EINVAL ; + +for (i =0 ; i <MODE_MAX ; i++) { +if (strcmp (mode, thread_mode_str[i]) ==0 ) { + hwlat_data.thread_mode = i; + ret = cnt; + } + } + + *ppos += cnt; + +return cnt; +} + +
Re: [RFC PATCH 2/5] tracing/hwlat: Implement the mode config option
Quoted 21 lines · Steven Rostedt
Quoted 18 lines · On Thu, 8 Apr 2021 16:13:20 +0200 Daniel Bristot de Oliveira…
kernel/trace/trace_hwlat.c +18
-0+/** +* hwlat_mode_write - Write function for "mode" entry +* @filp: The active open file structure +* @ubuf: The user buffer that contains the value to write +* @cnt: The maximum number of bytes to write to "file" +* @ppos: The current position in @file +* +* This function provides a write implementation for the "mode" interface +* to the hardware latency detector. hwlatd has different operation modes. +* The "none" sets the allowed cpumask for a single hwlatd thread at the +* startup and lets the scheduler handle the migration. The default mode is +* the "round-robin" one, in which a single hwlatd thread runs, migrating +* among the allowed CPUs in a round-robin fashion. +*/ +static ssize_t hwlat_mode_write (struct file *filp,const char __user *ubuf, +size_t cnt,loff_t *ppos) +{ +const char *mode;
Quoted 9 lines
kernel/trace/trace_hwlat.c hwlat_window_write(struct file *filp, const char __user *ubuf, · line 715
Quoted 6 lines
kernel/trace/trace_hwlat.c +6
-0+char buf[64 ]; +int ret; +int i; + +if (hwlat_busy) +return -EBUSY ;So we can't switch modes while running?
As you mentioned in the patch 3/5, this limitation was added because of the running threads. But, yes, stopping and starting the tracer to re-create the threads should work as well. I will try it for the next round.
Quoted 4 lines
Also, with this implemented, you can remove the
disable_migratevariable, and just switch the mode to NONE when it's detected that the affinity mask of the thread has been changed.
That was my initial intention with the NONE mode, but I feared breaking something by removing the "migrate_disable" logic. If you do not think it is a problem, I will remove the migrate disable and just change the mode.
-- Daniel
Quoted 30 lines
-- Steve
Quoted 26 lines
kernel/trace/trace_hwlat.c +26
-0+ +if (cnt >=sizeof (buf)) +return -EINVAL ; + +if (copy_from_user (buf, ubuf, cnt)) +return -EFAULT ; + + buf[cnt] =0 ; + + mode =strstrip (buf); + + ret = -EINVAL ; + +for (i =0 ; i <MODE_MAX ; i++) { +if (strcmp (mode, thread_mode_str[i]) ==0 ) { + hwlat_data.thread_mode = i; + ret = cnt; + } + } + + *ppos += cnt; + +return cnt; +} + +
Steven Rostedt
maintainerRe: [RFC PATCH 2/5] tracing/hwlat: Implement the mode config option
Quoted 3 lines · On Thu, 15 Apr 2021 15:16:04 +0200 Daniel Bristot de Oliveira…
That was my initial intention with the NONE mode, but I feared breaking something by removing the "
migrate_disable" logic. If you do not think it is a problem, I will remove the migrate disable and just change the mode.
Yes, just change it.
Thanks,
-- Steve
RFC PATCH 3/5 tracing/hwlat: Implement the per-cpu mode
In reply to Daniel Bristot de OliveiraImplements the per-cpu mode in which a sampling thread is created for each cpu in the "cpus" (and tracing_mask).
The per-cpu mode has the potention to speed up the hwlat detection by running on multiple CPUs at the same time.
--- Documentation/trace/hwlat_detector.rst | 6 +- kernel/trace/trace_hwlat.c | 171 +++++++++++++++++++------ 2 files changed, 137 insertions(+), 40 deletions(-)
Documentation/trace/hwlat_detector.rst +4 -2
@@ -85,10 +85,12 @@ the available options are: - none: do not force migration - round-robin: migrate across each CPU specified in cpus between each window + - per-cpu: create a per-cpu thread for each cpu in cpus By default, hwlat detector will also obey the tracing_cpumask, so the thread will be placed only in the set of cpus that is both on the hwlat detector's cpus and in the global tracing_cpumask file. The user can overwrite the cpumask by setting it manually. Changing the hwlatd affinity externally, -e.g., via taskset tool, will disable the round-robin migration. - +e.g., via taskset tool, will disable the round-robin migration. In the +per-cpu mode, the per-cpu thread (hwlatd/CPU) will be pinned to its relative +cpu, and its affinity cannot be changed.
kernel/trace/trace_hwlat.c +133 -38
299 lines not on this page · raw message
Signature · 1 line
2.30.2
kernel test robot
build botRe: [RFC PATCH 3/5] tracing/hwlat: Implement the per-cpu mode
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch]
Hi Daniel,
[FYI, it's a private test report for your RFC patch.]
[auto build test WARNING on tip/perf/core]
[also build test WARNING on linux/master linus/master v5.12-rc6] [cannot apply to trace/for-next next-20210408]
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Daniel-Bristot-de-Oliveira/hwlat-improvements-and-osnoise-tracer/20210408-221655 base: https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git cface0326a6c2ae5c8f47bd466f07624b3e348a7 config: mips-randconfig-r002-20210408 (attached as .config) compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project 56ea2e2fdd691136d5e6631fa0e447173694b82c)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# install mips cross compiling tool for clang build
# apt-get install binutils-mips-linux-gnu
# https://github.com/0day-ci/linux/commit/4e2f5d30c69f77756e8cf223acf55c2aa2657393
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Daniel-Bristot-de-Oliveira/hwlat-improvements-and-osnoise-tracer/20210408-221655 git checkout 4e2f5d30c69f77756e8cf223acf55c2aa2657393
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=mips
If you fix the issue, kindly add following tag as appropriate
All warnings (new ones prefixed by >>):
>> kernel/trace/trace_hwlat.c:122:28: warning: no previous prototype for function 'get_cpu_data' [-Wmissing-prototypes]
struct hwlat_kthread_data *get_cpu_data(void)
^
kernel/trace/trace_hwlat.c:122:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
struct hwlat_kthread_data *get_cpu_data(void)
^
static
kernel/trace/trace_hwlat.c:496:25: error: incompatible pointer types passing 'struct cpumask **' to parameter of type 'cpumask_var_t *' (aka 'struct cpumask (*)[1]') [-Werror,-Wincompatible-pointer-types]
if (!alloc_cpumask_var(&this_cpumask, GFP_KERNEL))
^~~~~~~~~~~~~
include/linux/cpumask.h:767:53: note: passing argument to parameter 'mask' here
static inline bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags)
^1 warning and 1 error generated.
vim +/get_cpu_data +122 kernel/trace/trace_hwlat.c
121
> 122 struct hwlat_kthread_data *get_cpu_data(void)
123 {
124 if (hwlat_data.thread_mode == MODE_PER_CPU)
125 return this_cpu_ptr(&hwlat_per_cpu_data);
126 else
127 return &hwlat_single_cpu_data;
128 }
129
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
kernel test robot
build botRe: [RFC PATCH 3/5] tracing/hwlat: Implement the per-cpu mode
In reply to Daniel Bristot de Oliveira[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch]
Hi Daniel,
[FYI, it's a private test report for your RFC patch.]
[auto build test WARNING on tip/perf/core]
[also build test WARNING on linux/master linus/master v5.12-rc6] [cannot apply to trace/for-next next-20210408]
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Daniel-Bristot-de-Oliveira/hwlat-improvements-and-osnoise-tracer/20210408-221655 base: https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git cface0326a6c2ae5c8f47bd466f07624b3e348a7 config: openrisc-randconfig-r013-20210408 (attached as .config) compiler: or1k-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/4e2f5d30c69f77756e8cf223acf55c2aa2657393
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Daniel-Bristot-de-Oliveira/hwlat-improvements-and-osnoise-tracer/20210408-221655 git checkout 4e2f5d30c69f77756e8cf223acf55c2aa2657393
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=openrisc
If you fix the issue, kindly add following tag as appropriate
All warnings (new ones prefixed by >>):
>> kernel/trace/trace_hwlat.c:122:28: warning: no previous prototype for 'get_cpu_data' [-Wmissing-prototypes]
122 | struct hwlat_kthread_data *get_cpu_data(void)
| ^~~~~~~~~~~~
In file included from include/linux/err.h:5,
from include/linux/kthread.h:5,
from kernel/trace/trace_hwlat.c:40:
kernel/trace/trace_hwlat.c: In function 'start_per_cpu_kthreads':
kernel/trace/trace_hwlat.c:496:25: error: passing argument 1 of 'alloc_cpumask_var' from incompatible pointer type [-Werror=incompatible-pointer-types]
496 | if (!alloc_cpumask_var(&this_cpumask, GFP_KERNEL))
| ^~~~~~~~~~~~~
| |
| struct cpumask **
include/linux/compiler.h:58:52: note: in definition of macro '__trace_if_var'
58 | #define __trace_if_var(cond) (__builtin_constant_p(cond) ? (cond) : __trace_if_value(cond))
| ^~~~
kernel/trace/trace_hwlat.c:496:2: note: in expansion of macro 'if'
496 | if (!alloc_cpumask_var(&this_cpumask, GFP_KERNEL))
| ^~
In file included from include/linux/smp.h:13,
from include/linux/lockdep.h:14,
from include/linux/rcupdate.h:29,
from include/linux/rculist.h:11,
from include/linux/pid.h:5,
from include/linux/sched.h:14,
from include/linux/kthread.h:6,
from kernel/trace/trace_hwlat.c:40:
include/linux/cpumask.h:767:53: note: expected 'struct cpumask (*)[1]' but argument is of type 'struct cpumask **'
767 | static inline bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags)
| ~~~~~~~~~~~~~~~^~~~
In file included from include/linux/err.h:5,
from include/linux/kthread.h:5,
from kernel/trace/trace_hwlat.c:40:
kernel/trace/trace_hwlat.c:496:25: error: passing argument 1 of 'alloc_cpumask_var' from incompatible pointer type [-Werror=incompatible-pointer-types]
496 | if (!alloc_cpumask_var(&this_cpumask, GFP_KERNEL))
| ^~~~~~~~~~~~~
| |
| struct cpumask **
include/linux/compiler.h:58:61: note: in definition of macro '__trace_if_var'
58 | #define __trace_if_var(cond) (__builtin_constant_p(cond) ? (cond) : __trace_if_value(cond))
| ^~~~
kernel/trace/trace_hwlat.c:496:2: note: in expansion of macro 'if'
496 | if (!alloc_cpumask_var(&this_cpumask, GFP_KERNEL))
| ^~
In file included from include/linux/smp.h:13,
from include/linux/lockdep.h:14,
from include/linux/rcupdate.h:29,
from include/linux/rculist.h:11,
from include/linux/pid.h:5,
from include/linux/sched.h:14,
from include/linux/kthread.h:6,
from kernel/trace/trace_hwlat.c:40:
include/linux/cpumask.h:767:53: note: expected 'struct cpumask (*)[1]' but argument is of type 'struct cpumask **'
767 | static inline bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags)
| ~~~~~~~~~~~~~~~^~~~
In file included from include/linux/err.h:5,
from include/linux/kthread.h:5,
from kernel/trace/trace_hwlat.c:40:
kernel/trace/trace_hwlat.c:496:25: error: passing argument 1 of 'alloc_cpumask_var' from incompatible pointer type [-Werror=incompatible-pointer-types]
496 | if (!alloc_cpumask_var(&this_cpumask, GFP_KERNEL))
| ^~~~~~~~~~~~~
| |
| struct cpumask **
include/linux/compiler.h:69:3: note: in definition of macro '__trace_if_value'
69 | (cond) ? \
| ^~~~
include/linux/compiler.h:56:28: note: in expansion of macro '__trace_if_var'
56 | #define if(cond, ...) if ( __trace_if_var( !!(cond , ## __VA_ARGS__) ) )
| ^~~~~~~~~~~~~~
kernel/trace/trace_hwlat.c:496:2: note: in expansion of macro 'if'
496 | if (!alloc_cpumask_var(&this_cpumask, GFP_KERNEL))
| ^~
In file included from include/linux/smp.h:13,
from include/linux/lockdep.h:14,
from include/linux/rcupdate.h:29,
from include/linux/rculist.h:11,
from include/linux/pid.h:5,
from include/linux/sched.h:14,
from include/linux/kthread.h:6,
from kernel/trace/trace_hwlat.c:40:
include/linux/cpumask.h:767:53: note: expected 'struct cpumask (*)[1]' but argument is of type 'struct cpumask **'
767 | static inline bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags)
| ~~~~~~~~~~~~~~~^~~~
kernel/trace/trace_hwlat.c: In function 'hwlat_mode_write':
kernel/trace/trace_hwlat.c:800:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable]
800 | int ret;
| ^~~cc1: some warnings being treated as errors
vim +/get_cpu_data +122 kernel/trace/trace_hwlat.c
121
> 122 struct hwlat_kthread_data *get_cpu_data(void)
123 {
124 if (hwlat_data.thread_mode == MODE_PER_CPU)
125 return this_cpu_ptr(&hwlat_per_cpu_data);
126 else
127 return &hwlat_single_cpu_data;
128 }
129
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
kernel test robot
build botRe: [RFC PATCH 3/5] tracing/hwlat: Implement the per-cpu mode
In reply to Daniel Bristot de Oliveira[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch]
Hi Daniel,
[FYI, it's a private test report for your RFC patch.]
[auto build test ERROR on tip/perf/core]
[also build test ERROR on linux/master linus/master v5.12-rc6] [cannot apply to trace/for-next next-20210408]
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Daniel-Bristot-de-Oliveira/hwlat-improvements-and-osnoise-tracer/20210408-221655 base: https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git cface0326a6c2ae5c8f47bd466f07624b3e348a7 config: mips-randconfig-r002-20210408 (attached as .config) compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project 56ea2e2fdd691136d5e6631fa0e447173694b82c)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# install mips cross compiling tool for clang build
# apt-get install binutils-mips-linux-gnu
# https://github.com/0day-ci/linux/commit/4e2f5d30c69f77756e8cf223acf55c2aa2657393
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Daniel-Bristot-de-Oliveira/hwlat-improvements-and-osnoise-tracer/20210408-221655 git checkout 4e2f5d30c69f77756e8cf223acf55c2aa2657393
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=mips
If you fix the issue, kindly add following tag as appropriate
All errors (new ones prefixed by >>):
kernel/trace/trace_hwlat.c:122:28: warning: no previous prototype for function 'get_cpu_data' [-Wmissing-prototypes] struct hwlat_kthread_data *get_cpu_data(void)
^
kernel/trace/trace_hwlat.c:122:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
struct hwlat_kthread_data *get_cpu_data(void)
^
static
>> kernel/trace/trace_hwlat.c:496:25: error: incompatible pointer types passing 'struct cpumask **' to parameter of type 'cpumask_var_t *' (aka 'struct cpumask (*)[1]') [-Werror,-Wincompatible-pointer-types]
if (!alloc_cpumask_var(&this_cpumask, GFP_KERNEL))
^~~~~~~~~~~~~
include/linux/cpumask.h:767:53: note: passing argument to parameter 'mask' here
static inline bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags)
^1 warning and 1 error generated.
vim +496 kernel/trace/trace_hwlat.c
480
481 /**
482 * start_per_cpu_kthread - Kick off the hardware latency sampling/detector kthreads
483 *
484 * This starts the kernel threads that will sit on potentially all cpus and
485 * sample the CPU timestamp counter (TSC or similar) and look for potential
486 * hardware latencies.
487 */
488 static int start_per_cpu_kthreads(struct trace_array *tr)
489 {
490 struct cpumask *current_mask = &save_cpumask;
491 struct cpumask *this_cpumask;
492 struct task_struct *kthread;
493 char comm[24];
494 int cpu;
495
> 496 if (!alloc_cpumask_var(&this_cpumask, GFP_KERNEL))
497 return -ENOMEM;
498
499 get_online_cpus();
500 /*
501 * Run only on CPUs in which trace and hwlat are allowed to run.
502 */
503 cpumask_and(current_mask, tr->tracing_cpumask, &hwlat_cpumask);
504 /*
505 * And the CPU is online.
506 */
507 cpumask_and(current_mask, cpu_online_mask, current_mask);
508 put_online_cpus();
509
510 for_each_online_cpu(cpu)
511 per_cpu(hwlat_per_cpu_data, cpu).kthread = NULL;
512
513 for_each_cpu(cpu, current_mask) {
514 snprintf(comm, 24, "hwlatd/%d", cpu);
515
516 kthread = kthread_create_on_cpu(kthread_fn, NULL, cpu, comm);
517 if (IS_ERR(kthread)) {
518 pr_err(BANNER "could not start sampling thread\n");
519 stop_per_cpu_kthreads();
520 return -ENOMEM;
521 }
522
523 per_cpu(hwlat_per_cpu_data, cpu).kthread = kthread;
524 wake_up_process(kthread);
525 }
526
527 return 0;
528 }
529
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
Steven Rostedt
maintainerRe: [RFC PATCH 3/5] tracing/hwlat: Implement the per-cpu mode
In reply to Daniel Bristot de OliveiraQuoted 5 lines · On Thu, 8 Apr 2021 16:13:21 +0200 Daniel Bristot de Oliveira…
Implements the per-cpu mode in which a sampling thread is created for each cpu in the "cpus" (and
tracing_mask).The per-cpu mode has the potention to speed up the hwlat detection by running on multiple CPUs at the same time.
And totally slow down the entire system in the process ;-)
Quoted 43 lines
Cc Jonathan Corbet <[email protected]>Cc Steven Rostedt <[email protected]>Cc Ingo Molnar <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Thomas Gleixner <[email protected]>Cc Alexandre Chartre <[email protected]>Cc Clark Willaims <[email protected]>Cc John Kacur <[email protected]>Cc Juri Lelli <[email protected]>Signed-off-by Daniel Bristot de Oliveira <[email protected]>--- Documentation/trace/hwlat_detector.rst | 6 +- kernel/trace/trace_hwlat.c | 171 +++++++++++++++++++------ 2 files changed, 137 insertions(+), 40 deletions(-)Documentation/trace/hwlat_detector.rst +4
-2@@ -85,10 +85,12 @@ the available options are: - none: do not force migration - round-robin: migrate across each CPU specified in cpus between each window + - per-cpu: create a per-cpu thread for each cpu in cpus By default, hwlat detector will also obey the tracing_cpumask, so the thread will be placed only in the set of cpus that is both on the hwlat detector's cpus and in the global tracing_cpumask file. The user can overwrite the cpumask by setting it manually. Changing the hwlatd affinity externally, -e.g., via taskset tool, will disable the round-robin migration. - +e.g., via taskset tool, will disable the round-robin migration. In the +per-cpu mode, the per-cpu thread (hwlatd/CPU) will be pinned to its relative +cpu, and its affinity cannot be changed.kernel/trace/trace_hwlat.c +0
-0
kernel/trace/trace_hwlat.c line 37
Quoted 5 lines
kernel/trace/trace_hwlat.c +1
-1@@ -34,7 +34,7 @@* Copyright (C) 2008-2009 Jon Masters, Red Hat, Inc. <[email protected]> * Copyright (C) 2013-2016 Steven Rostedt, Red Hat, Inc. <[email protected]> * -* Includes useful feedback from Clark Williams <[email protected]> +* Includes useful feedback from Clark Williams <[email protected]>
Interesting update ;-)
Quoted 202 lines
kernel/trace/trace_hwlat.c +64
-30* */ #include <linux/kthread.h> @@ -54,9 +54,6 @@ static struct trace_array *hwlat_trace;#define DEFAULT_SAMPLE_WIDTH 500000/* 0.5s */ #define DEFAULT_LAT_THRESHOLD 10/* 10us */ -/* sampling thread*/ -static struct task_struct *hwlat_kthread; -static struct dentry *hwlat_sample_width;/* sample width us */ static struct dentry *hwlat_sample_window;/* sample window us */ static struct dentry *hwlat_cpumask_dentry;/* hwlat cpus allowed */ @@ -65,19 +62,27 @@ static struct dentry *hwlat_thread_mode; /* hwlat thread mode */enum {MODE_NONE =0 ,MODE_ROUND_ROBIN , +MODE_PER_CPU ,MODE_MAX }; -static char *thread_mode_str[] = {"none" ,"round-robin" }; +static char *thread_mode_str[] = {"none" ,"round-robin" ,"per-cpu" };/* Save the previous tracing_thresh value */ static unsigned long save_tracing_thresh; -/* NMI timestamp counters */ -static u64 nmi_ts_start; -static u64 nmi_total_ts; -static int nmi_count; -static int nmi_cpu; +/* runtime kthread data */ +struct hwlat_kthread_data { +struct task_struct *kthread; +/* NMI timestamp counters */ +u64 nmi_ts_start; +u64 nmi_total_ts; +int nmi_count; +int nmi_cpu; +}; + +struct hwlat_kthread_data hwlat_single_cpu_data; +DEFINE_PER_CPU(struct hwlat_kthread_data, hwlat_per_cpu_data);/* Tells NMIs to call back to the hwlat tracer to record timestamps */ bool trace_hwlat_callback_enabled;@@ -114,6 +119,14 @@ static struct hwlat_data { .thread_mode = MODE_ROUND_ROBIN }; +struct hwlat_kthread_data *get_cpu_data (void ) +{ +if (hwlat_data.thread_mode ==MODE_PER_CPU ) +return this_cpu_ptr (&hwlat_per_cpu_data); +else +return &hwlat_single_cpu_data; +} +static bool hwlat_busy;static void trace_hwlat_sample (struct hwlat_sample *sample)@@ -151,7 +164,9 @@ static void trace_hwlat_sample(struct hwlat_sample *sample)void trace_hwlat_callback (bool enter) { -if (smp_processor_id () != nmi_cpu) +struct hwlat_kthread_data *kdata =get_cpu_data (); + +if (kdata->kthread)return ;/* @@ -160,13 +175,13 @@ void trace_hwlat_callback(bool enter)*/ if (!IS_ENABLED (CONFIG_GENERIC_SCHED_CLOCK )) {if (enter) - nmi_ts_start =time_get (); + kdata->nmi_ts_start =time_get ();else - nmi_total_ts +=time_get () - nmi_ts_start; + kdata->nmi_total_ts +=time_get () - kdata->nmi_ts_start; }if (enter) - nmi_count++; + kdata->nmi_count++; }/** @@ -178,6 +193,7 @@ void trace_hwlat_callback(bool enter)*/ static int get_sample (void ) { +struct hwlat_kthread_data *kdata =get_cpu_data ();struct trace_array *tr = hwlat_trace;struct hwlat_sample s;time_type start, t1, t2, last_t2;@@ -190,9 +206,8 @@ static int get_sample(void)do_div (thresh,NSEC_PER_USEC );/* modifies interval value */ - nmi_cpu =smp_processor_id (); - nmi_total_ts =0 ; - nmi_count =0 ; + kdata->nmi_total_ts =0 ; + kdata->nmi_count =0 ;/* Make sure NMIs see this first */ barrier ();@@ -262,15 +277,15 @@ static int get_sample(void) ret =1 ;/* We read in microseconds */ -if (nmi_total_ts) -do_div (nmi_total_ts,NSEC_PER_USEC ); +if (kdata->nmi_total_ts) +do_div (kdata->nmi_total_ts,NSEC_PER_USEC ); hwlat_data.count++; s.seqnum = hwlat_data.count; s.duration = sample; s.outer_duration = outer_sample; - s.nmi_total_ts = nmi_total_ts; - s.nmi_count = nmi_count; + s.nmi_total_ts = kdata->nmi_total_ts; + s.nmi_count = kdata->nmi_count; s.count = count;trace_hwlat_sample (&s);@@ -376,23 +391,43 @@ static int kthread_fn(void *data) }/** -* start_kthread - Kick off the hardware latency sampling/detector kthread +* stop_stop_kthread - Inform the hardware latency samping/detector kthread to stop +* +* This kicks the running hardware latency sampling/detector kernel thread and +* tells it to stop sampling now. Use this on unload and at system shutdown. +*/ +static void stop_single_kthread (void ) +{ +struct hwlat_kthread_data *kdata =get_cpu_data (); +struct task_struct *kthread = kdata->kthread; + +if (!kthread) + +return ; +kthread_stop (kthread); + + kdata->kthread =NULL ; +} + + +/** +* start_single_kthread - Kick off the hardware latency sampling/detector kthread * * This starts the kernel thread that will sit and sample the CPU timestamp * counter (TSC or similar) and look for potential hardware latencies. */ -static int start_kthread (struct trace_array *tr) +static int start_single_kthread (struct trace_array *tr) { +struct hwlat_kthread_data *kdata =get_cpu_data ();struct cpumask *current_mask = &save_cpumask;struct task_struct *kthread;int next_cpu; -if (hwlat_kthread) +if (kdata->kthread)return 0 ; - kthread =kthread_create (kthread_fn,NULL ,"hwlatd" ); -if (IS_ERR (kthread)) { +if (IS_ERR (kdata->kthread)) {pr_err (BANNER "could not start sampling thread\n" );return -ENOMEM ; }@@ -419,24 +454,77 @@ static int start_kthread(struct trace_array *tr)sched_setaffinity (kthread->pid, current_mask); - hwlat_kthread = kthread; + kdata->kthread = kthread;wake_up_process (kthread);return 0 ; }/** -* stop_kthread - Inform the hardware latency samping/detector kthread to stop +* stop_per_cpu_kthread - Inform the hardware latency samping/detector kthread to stop * -* This kicks the running hardware latency sampling/detector kernel thread and +* This kicks the running hardware latency sampling/detector kernel threads and * tells it to stop sampling now. Use this on unload and at system shutdown. */ -static void stop_kthread (void ) +static void stop_per_cpu_kthreads (void ) { -if (!hwlat_kthread)
kernel/trace/trace_hwlat.c static int start_kthread(struct trace_array *tr) · line 477
Quoted 10 lines
kernel/trace/trace_hwlat.c +7
-3-return ; -kthread_stop (hwlat_kthread); - hwlat_kthread =NULL ; +struct task_struct *kthread; +int cpu; + +for_each_online_cpu (cpu) { + kthread =per_cpu (hwlat_per_cpu_data, cpu).kthread; +if (kthread) +kthread_stop (kthread);
Probably want:
per_cpu(hwlat_per_cpu_data, cpu).kthread = NULL;
Just to be safe. I don't like to rely on the start doing the job, as things can change in the future. Having the clearing here as well makes the code more robust.
Quoted 73 lines
kernel/trace/trace_hwlat.c +56
-2+ } +} + +/** +* start_per_cpu_kthread - Kick off the hardware latency sampling/detector kthreads +* +* This starts the kernel threads that will sit on potentially all cpus and +* sample the CPU timestamp counter (TSC or similar) and look for potential +* hardware latencies. +*/ +static int start_per_cpu_kthreads (struct trace_array *tr) +{ +struct cpumask *current_mask = &save_cpumask; +struct cpumask *this_cpumask; +struct task_struct *kthread; +char comm[24 ]; +int cpu; + +if (!alloc_cpumask_var (&this_cpumask,GFP_KERNEL )) +return -ENOMEM ; + +get_online_cpus (); +/* +* Run only on CPUs in which trace and hwlat are allowed to run. +*/ +cpumask_and (current_mask, tr->tracing_cpumask, &hwlat_cpumask); +/* +* And the CPU is online. +*/ +cpumask_and (current_mask, cpu_online_mask, current_mask); +put_online_cpus (); + +for_each_online_cpu (cpu) +per_cpu (hwlat_per_cpu_data, cpu).kthread =NULL ; + +for_each_cpu (cpu, current_mask) { +snprintf (comm,24 ,"hwlatd/%d" , cpu); + + kthread =kthread_create_on_cpu (kthread_fn,NULL , cpu, comm); +if (IS_ERR (kthread)) { +pr_err (BANNER "could not start sampling thread\n" ); +stop_per_cpu_kthreads (); +return -ENOMEM ; + } + +per_cpu (hwlat_per_cpu_data, cpu).kthread = kthread; +wake_up_process (kthread); + } + +return 0 ; }/* @@ -701,7 +789,8 @@ static int hwlat_mode_open(struct inode *inode, struct file *file)* The "none" sets the allowed cpumask for a single hwlatd thread at the * startup and lets the scheduler handle the migration. The default mode is * the "round-robin" one, in which a single hwlatd thread runs, migrating -* among the allowed CPUs in a round-robin fashion. +* among the allowed CPUs in a round-robin fashion. The "per-cpu" mode +* creates one hwlatd thread per allowed CPU. */ static ssize_t hwlat_mode_write (struct file *filp,const char __user *ubuf,size_t cnt,loff_t *ppos)@@ -827,14 +916,20 @@ static void hwlat_tracer_start(struct trace_array *tr) {int err; - err =start_kthread (tr); +if (hwlat_data.thread_mode ==MODE_PER_CPU ) + err =start_per_cpu_kthreads (tr); +else + err =start_single_kthread (tr);if (err)
kernel/trace/trace_hwlat.c static void hwlat_tracer_start(struct trace_array *tr) · line 932
Quoted 10 lines
kernel/trace/trace_hwlat.c +4
-1pr_err (BANNER "Cannot start hwlat kthread\n" ); }static void hwlat_tracer_stop (struct trace_array *tr) { -stop_kthread (); +if (hwlat_data.thread_mode ==MODE_PER_CPU ) +stop_per_cpu_kthreads (); +else +stop_single_kthread ();
This explains why you have the "busy" check in the changing of the modes. But really, I don't see why you cant change the mode. Just stop the previous mode, and start the new one.
-- Steve
Quoted 12 lines
kernel/trace/trace_hwlat.c +1
-1}static int hwlat_tracer_init (struct trace_array *tr)@@ -864,7 +959,7 @@ static int hwlat_tracer_init(struct trace_array *tr)static void hwlat_tracer_reset (struct trace_array *tr) { -stop_kthread (); +hwlat_tracer_stop (tr);/* the tracing threshold is static between runs */ last_tracing_thresh = tracing_thresh;
Re: [RFC PATCH 3/5] tracing/hwlat: Implement the per-cpu mode
Quoted 10 lines · Steven Rostedt
Quoted 5 lines · On Thu, 8 Apr 2021 16:13:21 +0200 Daniel Bristot de Oliveira…
Implements the per-cpu mode in which a sampling thread is created for each cpu in the "cpus" (and
tracing_mask).The per-cpu mode has the potention to speed up the hwlat detection by running on multiple CPUs at the same time.
And totally slow down the entire system in the process ;-)
Too :-) But this is not the default config... So it should be an intentional change.
Quoted 42 lines
Quoted 42 lines
Cc Jonathan Corbet <[email protected]>Cc Steven Rostedt <[email protected]>Cc Ingo Molnar <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Thomas Gleixner <[email protected]>Cc Alexandre Chartre <[email protected]>Cc Clark Willaims <[email protected]>Cc John Kacur <[email protected]>Cc Juri Lelli <[email protected]>Signed-off-by Daniel Bristot de Oliveira <[email protected]>--- Documentation/trace/hwlat_detector.rst | 6 +- kernel/trace/trace_hwlat.c | 171 +++++++++++++++++++------ 2 files changed, 137 insertions(+), 40 deletions(-)Documentation/trace/hwlat_detector.rst +4
-2@@ -85,10 +85,12 @@ the available options are: - none: do not force migration - round-robin: migrate across each CPU specified in cpus between each window + - per-cpu: create a per-cpu thread for each cpu in cpus By default, hwlat detector will also obey the tracing_cpumask, so the thread will be placed only in the set of cpus that is both on the hwlat detector's cpus and in the global tracing_cpumask file. The user can overwrite the cpumask by setting it manually. Changing the hwlatd affinity externally, -e.g., via taskset tool, will disable the round-robin migration. - +e.g., via taskset tool, will disable the round-robin migration. In the +per-cpu mode, the per-cpu thread (hwlatd/CPU) will be pinned to its relative +cpu, and its affinity cannot be changed.kernel/trace/trace_hwlat.c +0
-0
Quoted 8 lines
kernel/trace/trace_hwlat.c line 37
Quoted 6 lines
kernel/trace/trace_hwlat.c +1
-1@@ -34,7 +34,7 @@* Copyright (C) 2008-2009 Jon Masters, Red Hat, Inc. <[email protected]> * Copyright (C) 2013-2016 Steven Rostedt, Red Hat, Inc. <[email protected]> * -* Includes useful feedback from Clark Williams <[email protected]> +* Includes useful feedback from Clark Williams <[email protected]> Interesting update ;-)
Should I make it a separated patch? :-)
Quoted 215 lines
Quoted 202 lines
kernel/trace/trace_hwlat.c +64
-30* */ #include <linux/kthread.h> @@ -54,9 +54,6 @@ static struct trace_array *hwlat_trace;#define DEFAULT_SAMPLE_WIDTH 500000/* 0.5s */ #define DEFAULT_LAT_THRESHOLD 10/* 10us */ -/* sampling thread*/ -static struct task_struct *hwlat_kthread; -static struct dentry *hwlat_sample_width;/* sample width us */ static struct dentry *hwlat_sample_window;/* sample window us */ static struct dentry *hwlat_cpumask_dentry;/* hwlat cpus allowed */ @@ -65,19 +62,27 @@ static struct dentry *hwlat_thread_mode; /* hwlat thread mode */enum {MODE_NONE =0 ,MODE_ROUND_ROBIN , +MODE_PER_CPU ,MODE_MAX }; -static char *thread_mode_str[] = {"none" ,"round-robin" }; +static char *thread_mode_str[] = {"none" ,"round-robin" ,"per-cpu" };/* Save the previous tracing_thresh value */ static unsigned long save_tracing_thresh; -/* NMI timestamp counters */ -static u64 nmi_ts_start; -static u64 nmi_total_ts; -static int nmi_count; -static int nmi_cpu; +/* runtime kthread data */ +struct hwlat_kthread_data { +struct task_struct *kthread; +/* NMI timestamp counters */ +u64 nmi_ts_start; +u64 nmi_total_ts; +int nmi_count; +int nmi_cpu; +}; + +struct hwlat_kthread_data hwlat_single_cpu_data; +DEFINE_PER_CPU(struct hwlat_kthread_data, hwlat_per_cpu_data);/* Tells NMIs to call back to the hwlat tracer to record timestamps */ bool trace_hwlat_callback_enabled;@@ -114,6 +119,14 @@ static struct hwlat_data { .thread_mode = MODE_ROUND_ROBIN }; +struct hwlat_kthread_data *get_cpu_data (void ) +{ +if (hwlat_data.thread_mode ==MODE_PER_CPU ) +return this_cpu_ptr (&hwlat_per_cpu_data); +else +return &hwlat_single_cpu_data; +} +static bool hwlat_busy;static void trace_hwlat_sample (struct hwlat_sample *sample)@@ -151,7 +164,9 @@ static void trace_hwlat_sample(struct hwlat_sample *sample)void trace_hwlat_callback (bool enter) { -if (smp_processor_id () != nmi_cpu) +struct hwlat_kthread_data *kdata =get_cpu_data (); + +if (kdata->kthread)return ;/* @@ -160,13 +175,13 @@ void trace_hwlat_callback(bool enter)*/ if (!IS_ENABLED (CONFIG_GENERIC_SCHED_CLOCK )) {if (enter) - nmi_ts_start =time_get (); + kdata->nmi_ts_start =time_get ();else - nmi_total_ts +=time_get () - nmi_ts_start; + kdata->nmi_total_ts +=time_get () - kdata->nmi_ts_start; }if (enter) - nmi_count++; + kdata->nmi_count++; }/** @@ -178,6 +193,7 @@ void trace_hwlat_callback(bool enter)*/ static int get_sample (void ) { +struct hwlat_kthread_data *kdata =get_cpu_data ();struct trace_array *tr = hwlat_trace;struct hwlat_sample s;time_type start, t1, t2, last_t2;@@ -190,9 +206,8 @@ static int get_sample(void)do_div (thresh,NSEC_PER_USEC );/* modifies interval value */ - nmi_cpu =smp_processor_id (); - nmi_total_ts =0 ; - nmi_count =0 ; + kdata->nmi_total_ts =0 ; + kdata->nmi_count =0 ;/* Make sure NMIs see this first */ barrier ();@@ -262,15 +277,15 @@ static int get_sample(void) ret =1 ;/* We read in microseconds */ -if (nmi_total_ts) -do_div (nmi_total_ts,NSEC_PER_USEC ); +if (kdata->nmi_total_ts) +do_div (kdata->nmi_total_ts,NSEC_PER_USEC ); hwlat_data.count++; s.seqnum = hwlat_data.count; s.duration = sample; s.outer_duration = outer_sample; - s.nmi_total_ts = nmi_total_ts; - s.nmi_count = nmi_count; + s.nmi_total_ts = kdata->nmi_total_ts; + s.nmi_count = kdata->nmi_count; s.count = count;trace_hwlat_sample (&s);@@ -376,23 +391,43 @@ static int kthread_fn(void *data) }/** -* start_kthread - Kick off the hardware latency sampling/detector kthread +* stop_stop_kthread - Inform the hardware latency samping/detector kthread to stop +* +* This kicks the running hardware latency sampling/detector kernel thread and +* tells it to stop sampling now. Use this on unload and at system shutdown. +*/ +static void stop_single_kthread (void ) +{ +struct hwlat_kthread_data *kdata =get_cpu_data (); +struct task_struct *kthread = kdata->kthread; + +if (!kthread) + +return ; +kthread_stop (kthread); + + kdata->kthread =NULL ; +} + + +/** +* start_single_kthread - Kick off the hardware latency sampling/detector kthread * * This starts the kernel thread that will sit and sample the CPU timestamp * counter (TSC or similar) and look for potential hardware latencies. */ -static int start_kthread (struct trace_array *tr) +static int start_single_kthread (struct trace_array *tr) { +struct hwlat_kthread_data *kdata =get_cpu_data ();struct cpumask *current_mask = &save_cpumask;struct task_struct *kthread;int next_cpu; -if (hwlat_kthread) +if (kdata->kthread)return 0 ; - kthread =kthread_create (kthread_fn,NULL ,"hwlatd" ); -if (IS_ERR (kthread)) { +if (IS_ERR (kdata->kthread)) {pr_err (BANNER "could not start sampling thread\n" );return -ENOMEM ; }@@ -419,24 +454,77 @@ static int start_kthread(struct trace_array *tr)sched_setaffinity (kthread->pid, current_mask); - hwlat_kthread = kthread; + kdata->kthread = kthread;wake_up_process (kthread);return 0 ; }/** -* stop_kthread - Inform the hardware latency samping/detector kthread to stop +* stop_per_cpu_kthread - Inform the hardware latency samping/detector kthread to stop * -* This kicks the running hardware latency sampling/detector kernel thread and +* This kicks the running hardware latency sampling/detector kernel threads and * tells it to stop sampling now. Use this on unload and at system shutdown. */ -static void stop_kthread (void ) +static void stop_per_cpu_kthreads (void ) { -if (!hwlat_kthread)kernel/trace/trace_hwlat.c static int start_kthread(struct trace_array *tr) · line 477
Quoted 10 lines
kernel/trace/trace_hwlat.c +7
-3-return ; -kthread_stop (hwlat_kthread); - hwlat_kthread =NULL ; +struct task_struct *kthread; +int cpu; + +for_each_online_cpu (cpu) { + kthread =per_cpu (hwlat_per_cpu_data, cpu).kthread; +if (kthread) +kthread_stop (kthread);Probably want:
Quoted 5 lines
per_cpu(hwlat_per_cpu_data, cpu).kthread = NULL;Just to be safe. I don't like to rely on the start doing the job, as things can change in the future. Having the clearing here as well makes the code more robust.
Ack!
Quoted 77 lines
Quoted 77 lines
kernel/trace/trace_hwlat.c +56
-2+ } +} + +/** +* start_per_cpu_kthread - Kick off the hardware latency sampling/detector kthreads +* +* This starts the kernel threads that will sit on potentially all cpus and +* sample the CPU timestamp counter (TSC or similar) and look for potential +* hardware latencies. +*/ +static int start_per_cpu_kthreads (struct trace_array *tr) +{ +struct cpumask *current_mask = &save_cpumask; +struct cpumask *this_cpumask; +struct task_struct *kthread; +char comm[24 ]; +int cpu; + +if (!alloc_cpumask_var (&this_cpumask,GFP_KERNEL )) +return -ENOMEM ; + +get_online_cpus (); +/* +* Run only on CPUs in which trace and hwlat are allowed to run. +*/ +cpumask_and (current_mask, tr->tracing_cpumask, &hwlat_cpumask); +/* +* And the CPU is online. +*/ +cpumask_and (current_mask, cpu_online_mask, current_mask); +put_online_cpus (); + +for_each_online_cpu (cpu) +per_cpu (hwlat_per_cpu_data, cpu).kthread =NULL ; + +for_each_cpu (cpu, current_mask) { +snprintf (comm,24 ,"hwlatd/%d" , cpu); + + kthread =kthread_create_on_cpu (kthread_fn,NULL , cpu, comm); +if (IS_ERR (kthread)) { +pr_err (BANNER "could not start sampling thread\n" ); +stop_per_cpu_kthreads (); +return -ENOMEM ; + } + +per_cpu (hwlat_per_cpu_data, cpu).kthread = kthread; +wake_up_process (kthread); + } + +return 0 ; }/* @@ -701,7 +789,8 @@ static int hwlat_mode_open(struct inode *inode, struct file *file)* The "none" sets the allowed cpumask for a single hwlatd thread at the * startup and lets the scheduler handle the migration. The default mode is * the "round-robin" one, in which a single hwlatd thread runs, migrating -* among the allowed CPUs in a round-robin fashion. +* among the allowed CPUs in a round-robin fashion. The "per-cpu" mode +* creates one hwlatd thread per allowed CPU. */ static ssize_t hwlat_mode_write (struct file *filp,const char __user *ubuf,size_t cnt,loff_t *ppos)@@ -827,14 +916,20 @@ static void hwlat_tracer_start(struct trace_array *tr) {int err; - err =start_kthread (tr); +if (hwlat_data.thread_mode ==MODE_PER_CPU ) + err =start_per_cpu_kthreads (tr); +else + err =start_single_kthread (tr);if (err)pr_err (BANNER "Cannot start hwlat kthread\n" ); }
Quoted 11 lines
kernel/trace/trace_hwlat.c static void hwlat_tracer_start(struct trace_array *tr) · line 932
Quoted 7 lines
kernel/trace/trace_hwlat.c +4
-1static void hwlat_tracer_stop (struct trace_array *tr) { -stop_kthread (); +if (hwlat_data.thread_mode ==MODE_PER_CPU ) +stop_per_cpu_kthreads (); +else +stop_single_kthread ();This explains why you have the "busy" check in the changing of the modes. But really, I don't see why you cant change the mode. Just stop the previous mode, and start the new one.
I will try it!
Thanks!
-- Daniel
Quoted 16 lines
-- Steve
Quoted 12 lines
kernel/trace/trace_hwlat.c +1
-1}static int hwlat_tracer_init (struct trace_array *tr)@@ -864,7 +959,7 @@ static int hwlat_tracer_init(struct trace_array *tr)static void hwlat_tracer_reset (struct trace_array *tr) { -stop_kthread (); +hwlat_tracer_stop (tr);/* the tracing threshold is static between runs */ last_tracing_thresh = tracing_thresh;
Steven Rostedt
maintainerRe: [RFC PATCH 3/5] tracing/hwlat: Implement the per-cpu mode
Quoted 11 lines · On Thu, 15 Apr 2021 15:22:52 +0200 Daniel Bristot de Oliveira…
Quoted 10 lines
kernel/trace/trace_hwlat.c line 37
Quoted 8 lines
kernel/trace/trace_hwlat.c +1
-1@@ -34,7 +34,7 @@* Copyright (C) 2008-2009 Jon Masters, Red Hat, Inc. <[email protected]> * Copyright (C) 2013-2016 Steven Rostedt, Red Hat, Inc. <[email protected]> * -* Includes useful feedback from Clark Williams <[email protected]> +* Includes useful feedback from Clark Williams <[email protected]> Interesting update ;-)
Quoted 1 line
Should I make it a separated patch? :-)
Yeah, probably.
-- Steve
RFC PATCH 4/5 tracing: Add __print_ns_to_secs() and __print_ns_without_secs() helpers
In reply to Daniel Bristot de OliveiraFrom: Steven Rostedt <[email protected]>
To have nanosecond output displayed in a more human readable format, its nicer to convert it to a seconds format (XXX.YYYYYYYYY). The problem is that to do so, the numbers must be divided by NSEC_PER_SEC, and moded too. But as these numbers are 64 bit, this can not be done simply with '/' and '%' operators, but must use do_div() instead.
Instead of performing the expensive do_div() in the hot path of the tracepoint, it is more efficient to perform it during the output phase. But passing in do_div() can confuse the parser, and do_div() doesn't work exactly like a normal C function. It modifies the number in place, and we don't want to modify the actual values in the ring buffer.
Two helper functions are now created:
__print_ns_to_secs() and __print_ns_without_secs()
They both take a value of nanoseconds, and the former will return that number divided by NSEC_PER_SEC, and the latter will mod it with NSEC_PER_SEC giving a way to print a nice human readable format:
__print_fmt("time=%llu.%09u",
__print_ns_to_secs(REC->nsec_val), __print_ns_without_secs(REC->nsec_val))
--- include/trace/trace_events.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+)
include/trace/trace_events.h +25 -0
@@ -33,6 +33,21 @@static const char TRACE_SYSTEM_STRING [] = \ __stringify(TRACE_SYSTEM) +#undef __print_ns_to_secs +#define __print_ns_to_secs (value) \ + ({ \ + u64 ____val = (u64)value; \ + do_div(____val, NSEC_PER_SEC); \ + ____val; \ + }) + +#undef __print_ns_without_secs +#define __print_ns_without_secs (value) \ + ({ \ + u64 ____val = (u64)value; \ + (u32) do_div(____val, NSEC_PER_SEC); \ + }) +TRACE_MAKE_SYSTEM_STR ();#undef TRACE_DEFINE_ENUM
@@ -736,6 +751,16 @@ static inline void ftrace_test_probe_##call(void) \#undef __print_array#undef __print_hex_dump +/* +* The below is not executed in the kernel. It is only what is +* displayed in the print format for userspace to parse. +*/ +#undef __print_ns_to_secs +#define __print_ns_to_secs (val) val / 1000000000UL + +#undef __print_ns_without_secs +#define __print_ns_without_secs (val) val % 1000000000UL +#undef TP_printk #define TP_printk(fmt, args...) "\"" fmt "\", " __stringify(args)
Signature · 1 line
2.30.2
RFC PATCH 5/5 tracing: Add the osnoise tracer
In reply to Daniel Bristot de OliveiraIn the context of high-performance computing (HPC), the Operating System Noise (osnoise) refers to the interference experienced by an application due to activities inside the operating system. In the context of Linux, NMIs, IRQs, SoftIRQs, and any other system thread can cause noise to the system. Moreover, hardware-related jobs can also cause noise, for example, via SMIs.
hwlat_detector is one of the tools used to identify the most complex source of noise: hardware noise.
In a nutshell, the hwlat_detector creates a thread that runs periodically for a given period. At the beginning of a period, the thread disables interrupt and starts sampling. While running, the hwlatd thread reads the time in a loop. As interrupts are disabled, threads, IRQs, and SoftIRQs cannot interfere with the hwlatd thread. Hence, the cause of any gap between two different reads of the time roots either on NMI or in the hardware itself. At the end of the period, hwlatd enables interrupts and reports the max observed gap between the reads. It also prints an NMI occurrence counter. If the output does not report NMI executions, the user can conclude that the hardware is the culprit for the latency. The hwlat detects the NMI execution by observing the entry and exit of an NMI.
The osnoise tracer leverages the hwlat_detector by running a similar loop with preemption, SoftIRQs and IRQs enabled, thus allowing all the sources of osnoise during its execution. Using the same approach of hwlat, osnoise takes note of the entry and exit point of any source of interferences, increasing a per-cpu interference counter. The osnoise tracer also saves an interference counter for each source of interference. The interference counter for NMI, IRQs, SoftIRQs, and threads is increased anytime the tool observes these interferences' entry events. When a noise happens without any interference from the operating system level, the hardware noise counter increases, pointing to a hardware-related noise. In this way, osnoise can account for any source of interference. At the end of the period, the osnoise tracer prints the sum of all noise, the max single noise, the percentage of CPU available for the thread, and the counters for the noise sources.
Usage
Write the ASCII text osnoise into the current_tracer file of the tracing system (generally mounted at /sys/kernel/tracing or /sys/kernel/debug/tracing).
For example::
[root@f32 ~]# cd /sys/kernel/tracing/ [root@f32 tracing]# echo osnoise > current_tracer
It is possible to follow the trace by reading the trace trace file::
[root@f32 tracing]# cat trace
# tracer: osnoise
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth MAX
# || / SINGLE Interference counters:
# |||| RUNTIME NOISE % OF CPU NOISE +-----------------------------+
# TASK-PID CPU# |||| TIMESTAMP IN US IN US AVAILABLE IN US HW NMI IRQ SIRQ THREAD
# | | | |||| | | | | | | | | | |
<...>-859 [000] .... 81.637220: 1000000 190 99.98100 9 18 0 1007 18 1
<...>-860 [001] .... 81.638154: 1000000 656 99.93440 74 23 0 1006 16 3
<...>-861 [002] .... 81.638193: 1000000 5675 99.43250 202 6 0 1013 25 21
<...>-862 [003] .... 81.638242: 1000000 125 99.98750 45 1 0 1011 23 0
<...>-863 [004] .... 81.638260: 1000000 1721 99.82790 168 7 0 1002 49 41
<...>-864 [005] .... 81.638286: 1000000 263 99.97370 57 6 0 1006 26 2
<...>-865 [006] .... 81.638302: 1000000 109 99.98910 21 3 0 1006 18 1
<...>-866 [007] .... 81.638326: 1000000 7816 99.21840 107 8 0 1016 39 19In addition to the regular trace fields (from TASK-PID to TIMESTAMP), the tracer prints a message at the end of each period for each CPU that is running an osnoise/ thread. The osnoise specific fields report:
- The RUNTIME IN USE reports the amount of time in microseconds that the osnoise thread kept looping reading the time.
- The NOISE IN US reports the sum of noise in microseconds observed by the osnoise tracer during the associated runtime.
- The % OF CPU AVAILABLE reports the percentage of CPU available for the osnoise thread during the runtime window.
- The MAX SINGLE NOISE IN US reports the maximum single noise observed during the runtime window.
- The Interference counters display how many each of the respective interference happened during the runtime window.
Note that the example above shows a high number of HW noise samples. The reason being is that this sample was taken on a virtual machine, and the host interference is detected as a hardware interference.
Tracer options
The tracer has a set of options inside the osnoise directory, they are:
- cpus: CPUs at which a osnoise thread will execute.
- period_us: the period of the osnoise thread.
- runtime_us: how long an osnoise thread will look for noise.
- stop_tracing_single_us: stop the system tracing of a single noise higher than the configured value is happens. Writing 0 disables this option.
- stop_tracing_total_us: stop the system tracing of a NOISE IN USE higher than the configured value is happens. Writing 0 disables this option.
- tolerance_ns: the minimum delta between two time() reads to be considered as noise.
Additional Tracing
In addition to the tracer, a set of tracepoints were added to facilitate the identification of the osnoise source.
- osnoise:sample_threshold: printed anytime a noise is higher than the configurable tolerance_ns.
- osnoise:nmi_noise: noise from NMI, including the duration.
- osnoise:irq_noise: noise from an IRQ, including the duration.
- osnoise:softirq_noise: noise from a SoftIRQ, including the duration.
- osnoise:thread_noise: noise from a thread, including the duration.
Note that a all the values are net values. This means that a thread duration will not contain the duration of the IRQs that happened during its execution, for example. The same is valid for all duration values.
Here is one example of the usage of these tracepoints::
osnoise/8-961 [008] d.h. 5789.857532: irq_noise: local_timer:236 start 5789.857529929 duration 1845 ns
osnoise/8-961 [008] dNh. 5789.858408: irq_noise: local_timer:236 start 5789.858404871 duration 2848 ns
migration/8-54 [008] d... 5789.858413: thread_noise: migration/8:54 start 5789.858409300 duration 3068 ns
osnoise/8-961 [008] .... 5789.858413: sample_threshold: start 5789.858404555 duration 8 us interferences 2In this example, a noise sample of 8 microseconds was reported in the last fine, pointing to two interferences. Looking backward in the trace, the two previous entries were about the migration thread running after a timer IRQ execution. The first event is not part of the noise because it took place one millisecond before.
It is worth noticing that the sum of the duration reported in the tracepoints is smaller than eight us reported in the sample_threshold. The reason roots in the tracing overhead and in the overhead of the entry and exit code that happens before and after any interference execution. This justifies the dual approach: measuring thread and tracing.
--- Documentation/trace/osnoise_tracer.rst | 149 ++ include/linux/ftrace_irq.h | 16 + include/trace/events/osnoise.h | 141 ++ kernel/trace/Kconfig | 34 + kernel/trace/Makefile | 1 + kernel/trace/trace.h | 9 +- kernel/trace/trace_entries.h | 27 + kernel/trace/trace_osnoise.c | 1714 ++++++++++++++++++++++++ kernel/trace/trace_output.c | 72 +- 9 files changed, 2159 insertions(+), 4 deletions(-) create mode 100644 Documentation/trace/osnoise_tracer.rst create mode 100644 include/trace/events/osnoise.h create mode 100644 kernel/trace/trace_osnoise.c
Documentation/trace/osnoise_tracer.rst new file +149 -0
@@ -0,0 +1,149 @@ +============== +OSNOISE Tracer +============== + +In the context of high-performance computing (HPC), the Operating System +Noise (*osnoise*) refers to the interference experienced by an application +due to activities inside the operating system. In the context of Linux, +NMIs, IRQs, SoftIRQs, and any other system thread can cause noise to the +system. Moreover, hardware-related jobs can also cause noise, for example, +via SMIs. + +``hwlat_detector`` is one of the tools used to identify the most complex +source of noise: *hardware noise*. + +In a nutshell, the ``hwlat_detector`` creates a thread that runs +periodically for a given period. At the beginning of a period, the thread +disables interrupt and starts sampling. While running, the ``hwlatd`` +thread reads the time in a loop. As interrupts are disabled, threads, +IRQs, and SoftIRQs cannot interfere with the ``hwlatd`` thread. Hence, the +cause of any gap between two different reads of the time roots either on +NMI or in the hardware itself. At the end of the period, ``hwlatd`` enables +interrupts and reports the max observed gap between the reads. It also +prints a NMI occurrence counter. If the output does not report NMI +executions, the user can conclude that the hardware is the culprit for +the latency. The ``hwlat`` detects the NMI execution by observing +the entry and exit of a NMI. + +The ``osnoise`` tracer leverages the ``hwlat_detector`` by running a +similar loop with preemption, SoftIRQs and IRQs enabled, thus allowing +all the sources of *osnoise* during its execution. Using the same approach +of ``hwlat``, ``osnoise`` takes note of the entry and exit point of any +source of interferences, increasing a per-cpu interference counter. The +``osnoise`` tracer also saves an interference counter for each source of +interference. The interference counter for NMI, IRQs, SoftIRQs, and +threads is increased anytime the tool observes these interferences' entry +events. When a noise happens without any interference from the operating +system level, the hardware noise counter increases, pointing to a +hardware-related noise. In this way, ``osnoise`` can account for any +source of interference. At the end of the period, the ``osnoise`` tracer +prints the sum of all noise, the max single noise, the percentage of CPU +available for the thread, and the counters for the noise sources. + +Usage +----- + +Write the ASCII text ``osnoise`` into the ``current_tracer`` file of the +tracing system (generally mounted at ``/sys/kernel/tracing`` or +``/sys/kernel/debug/tracing``). + +For example:: + + [root@f32 ~]# cd /sys/kernel/tracing/ + [root@f32 tracing]# echo osnoise > current_tracer + +It is possible to follow the trace by reading the ``trace`` trace file:: + + [root@f32 tracing]# cat trace + # tracer: osnoise + # + # _-----=> irqs-off + # / _----=> need-resched + # | / _---=> hardirq/softirq + # || / _--=> preempt-depth MAX + # || / SINGLE Interference counters: + # |||| RUNTIME NOISE % OF CPU NOISE +-----------------------------+ + # TASK-PID CPU# |||| TIMESTAMP IN US IN US AVAILABLE IN US HW NMI IRQ SIRQ THREAD + # | | | |||| | | | | | | | | | | + <...>-859 [000] .... 81.637220: 1000000 190 99.98100 9 18 0 1007 18 1 + <...>-860 [001] .... 81.638154: 1000000 656 99.93440 74 23 0 1006 16 3 + <...>-861 [002] .... 81.638193: 1000000 5675 99.43250 202 6 0 1013 25 21 + <...>-862 [003] .... 81.638242: 1000000 125 99.98750 45 1 0 1011 23 0 + <...>-863 [004] .... 81.638260: 1000000 1721 99.82790 168 7 0 1002 49 41 + <...>-864 [005] .... 81.638286: 1000000 263 99.97370 57 6 0 1006 26 2 + <...>-865 [006] .... 81.638302: 1000000 109 99.98910 21 3 0 1006 18 1 + <...>-866 [007] .... 81.638326: 1000000 7816 99.21840 107 8 0 1016 39 19 + +In addition to the regular trace fields (from TASK-PID to TIMESTAMP), the +tracer prints a message at the end of each period for each CPU that is +running an ``osnoise/`` thread. The osnoise specific fields report: + + - The ``RUNTIME IN USE`` reports the amount of time in microseconds that + the ``osnoise`` thread kept looping reading the time. + - The ``NOISE IN US`` reports the sum of noise in microseconds observed + by the osnoise tracer during the associated runtime. + - The ``% OF CPU AVAILABLE`` reports the percentage of CPU available for + the ``osnoise`` thread during the ``runtime`` window. + - The ``MAX SINGLE NOISE IN US`` reports the maximum single noise observed + during the ``runtime`` window. + - The ``Interference counters`` display how many each of the respective + interference happened during the ``runtime`` window. + +Note that the example above shows a high number of ``HW noise`` samples. +The reason being is that this sample was taken on a virtual machine, +and the host interference is detected as a hardware interference. + +Tracer options +--------------------- + +The tracer has a set of options inside the ``osnoise`` directory, they are: + + - ``cpus``: CPUs at which a ``osnoise`` thread will execute. + - ``period_us``: the period of the ``osnoise`` thread. + - ``runtime_us``: how long an ``osnoise`` thread will look for noise. + - ``stop_tracing_single_us``: stop the system tracing of a single noise + higher than the configured value is happens. Writing ``0`` disables this + option. + - ``stop_tracing_total_us``: stop the system tracing of a ``NOISE IN USE`` + higher than the configured value is happens. Writing ``0`` disables this + option. + - ``tolerance_ns``: the minimum delta between two time() reads to be + considered as noise. + +Additional Tracing +------------------ + +In addition to the tracer, a set of ``tracepoints`` were added to +facilitate the identification of the osnoise source. + + - ``osnoise:sample_threshold``: printed anytime a noise is higher than + the configurable ``tolerance_ns``. + - ``osnoise:nmi_noise``: noise from NMI, including the duration. + - ``osnoise:irq_noise``: noise from an IRQ, including the duration. + - ``osnoise:softirq_noise``: noise from a SoftIRQ, including the + duration. + - ``osnoise:thread_noise``: noise from a thread, including the duration. + +Note that a all the values are *net values*. This means that a *thread* +duration will not contain the duration of the *IRQs* that happened during +its execution, for example. The same is valid for all duration values. + +Here is one example of the usage of these ``tracepoints``:: + + osnoise/8-961 [008] d.h. 5789.857532: irq_noise: local_timer:236 start 5789.857529929 duration 1845 ns + osnoise/8-961 [008] dNh. 5789.858408: irq_noise: local_timer:236 start 5789.858404871 duration 2848 ns + migration/8-54 [008] d... 5789.858413: thread_noise: migration/8:54 start 5789.858409300 duration 3068 ns + osnoise/8-961 [008] .... 5789.858413: sample_threshold: start 5789.858404555 duration 8 us interferences 2 + +In this example, a noise sample of 8 microseconds was reported in the last +fine, pointing to two interferences. Looking backward in the trace, the +two previous entries were about the ``migration`` thread running after +a timer IRQ execution. The first event is not part of the noise because +it took place one millisecond before. + +It is worth noticing that the sum of the duration reported in the +``tracepoints`` is smaller than eight us reported in the +``sample_threshold``. The reason roots in the tracing overhead and in +the overhead of the entry and exit code that happens before and after +any interference execution. This justifies the dual approach: measuring +thread and tracing.
include/linux/ftrace_irq.h +16 -0
@@ -7,12 +7,24 @@ extern bool trace_hwlat_callback_enabled;extern void trace_hwlat_callback (bool enter);#endif +/* +* XXX: Make it generic +*/ +#ifdef CONFIG_OSNOISE_TRACER +extern bool trace_osnoise_callback_enabled; +extern void trace_osnoise_callback (bool enter); +#endif +static inline void ftrace_nmi_enter (void ) {#ifdef CONFIG_HWLAT_TRACER if (trace_hwlat_callback_enabled)trace_hwlat_callback (true);#endif +#ifdef CONFIG_OSNOISE_TRACER +if (trace_osnoise_callback_enabled) +trace_osnoise_callback (true); +#endif }static inline void ftrace_nmi_exit (void )
@@ -21,6 +33,10 @@ static inline void ftrace_nmi_exit(void)if (trace_hwlat_callback_enabled)trace_hwlat_callback (false);#endif +#ifdef CONFIG_OSNOISE_TRACER +if (trace_osnoise_callback_enabled) +trace_osnoise_callback (false); +#endif }#endif /* _LINUX_FTRACE_IRQ_H */
include/trace/events/osnoise.h new file +141 -0
141 lines not on this page · raw message
kernel/trace/Kconfig +34 -0
@@ -356,6 +356,40 @@ config HWLAT_TRACER file. Every time a latency is greater than tracing_thresh, it will be recorded into the ring buffer. +config OSNOISE_TRACER + bool "OS Noise tracer" + select GENERIC_TRACER + help + In the context of high-performance computing (HPC), the Operating + System Noise (osnoise) refers to the interference experienced by an + application due to activities inside the operating system. In the + context of Linux, NMIs, IRQs, SoftIRQs, and any other system thread + can cause noise to the system. Moreover, hardware-related jobs can + also cause noise, for example, via SMIs. + + The osnoise tracer leverages the hwlat_detector by running a similar + loop with preemption, SoftIRQs and IRQs enabled, thus allowing all + the sources of osnoise during its execution. The osnoise tracer takes + note of the entry and exit point of any source of interferences, + increasing a per-cpu interference counter. It saves an interference + counter for each source of interference. The interference counter for + NMI, IRQs, SoftIRQs, and threads is increased anytime the tool + observes these interferences' entry events. When a noise happens + without any interference from the operating system level, the + hardware noise counter increases, pointing to a hardware-related + noise. In this way, osnoise can account for any source of + interference. At the end of the period, the osnoise tracer prints + the sum of all noise, the max single noise, the percentage of CPU + available for the thread, and the counters for the noise sources. + + In addition to the tracer, a set of tracepoints were added to + facilitate the identification of the osnoise source. + + The output will appear in the trace and trace_pipe files. + + To enable this tracer, echo in "osnoise" into the current_tracer + file. + config MMIOTRACE bool "Memory mapped IO tracing" depends on HAVE_MMIOTRACE_SUPPORT && PCI
kernel/trace/Makefile +1 -0
@@ -58,6 +58,7 @@ obj-$(CONFIG_IRQSOFF_TRACER) += trace_irqsoff.o obj-$(CONFIG_PREEMPT_TRACER) += trace_irqsoff.o obj-$(CONFIG_SCHED_TRACER) += trace_sched_wakeup.o obj-$(CONFIG_HWLAT_TRACER) += trace_hwlat.o +obj-$(CONFIG_OSNOISE_TRACER) += trace_osnoise.o obj-$(CONFIG_NOP_TRACER) += trace_nop.o obj-$(CONFIG_STACK_TRACER) += trace_stack.o obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o
kernel/trace/trace.h +6 -3
@@ -44,6 +44,7 @@ enum trace_type {TRACE_BLK ,TRACE_BPUTS ,TRACE_HWLAT , +TRACE_OSNOISE ,TRACE_RAW_DATA , __TRACE_LAST_TYPE,
@@ -285,7 +286,8 @@ struct trace_array {struct array_buffer max_buffer;bool allocated_snapshot;#endif -#if defined(CONFIG_TRACER_MAX_TRACE ) || defined(CONFIG_HWLAT_TRACER ) +#if defined(CONFIG_TRACER_MAX_TRACE ) || defined(CONFIG_HWLAT_TRACER ) \ + || defined(CONFIG_OSNOISE_TRACER )unsigned long max_latency;#ifdef CONFIG_FSNOTIFY struct dentry *d_max_latency;
@@ -431,6 +433,7 @@ extern void __ftrace_bad_type(void); IF_ASSIGN(var, ent, struct bprint_entry, TRACE_BPRINT); \ IF_ASSIGN(var, ent, struct bputs_entry, TRACE_BPUTS); \ IF_ASSIGN(var, ent, struct hwlat_entry, TRACE_HWLAT); \ + IF_ASSIGN(var, ent, struct osnoise_entry, TRACE_OSNOISE);\ IF_ASSIGN(var, ent, struct raw_data_entry, TRACE_RAW_DATA);\ IF_ASSIGN(var, ent, struct trace_mmiotrace_rw, \TRACE_MMIO_RW ); \
@@ -656,8 +659,8 @@ void update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu);#endif /* CONFIG_TRACER_MAX_TRACE */ -#if (defined(CONFIG_TRACER_MAX_TRACE ) || defined(CONFIG_HWLAT_TRACER )) && \ - defined(CONFIG_FSNOTIFY ) +#if (defined(CONFIG_TRACER_MAX_TRACE ) || defined(CONFIG_HWLAT_TRACER )) \ + || defined(CONFIG_OSNOISE_TRACER ) && defined(CONFIG_FSNOTIFY )void latency_fsnotify (struct trace_array *tr);
kernel/trace/trace_entries.h +27 -0
@@ -338,3 +338,30 @@ FTRACE_ENTRY(hwlat, hwlat_entry, __entry->nmi_total_ts, __entry->nmi_count) ); + +FTRACE_ENTRY (osnoise, osnoise_entry, + +TRACE_OSNOISE , + +F_STRUCT ( + __field( u64, noise ) + __field( u64, runtime ) + __field( u64, max_sample ) + __field( unsigned int, count ) + __field( unsigned int, hw_count ) + __field( unsigned int, nmi_count ) + __field( unsigned int, irq_count ) + __field( unsigned int, softirq_count ) + __field( unsigned int, thread_count ) + ), + +F_printk ("noise:%llu\tmax_sample:%llu\tcount:%d\thw:%u\tnmi:%u\tirq:%u\tsoftirq:%u\tthread:%u\n" , + __entry->noise, + __entry->max_sample, + __entry->count, + __entry->hw_count, + __entry->nmi_count, + __entry->irq_count, + __entry->softirq_count, + __entry->thread_count) +);
kernel/trace/trace_osnoise.c new file +1714 -0
1,714 lines not on this page · raw message
kernel/trace/trace_output.c +71 -1
90 lines not on this page · raw message
Signature · 1 line
2.30.2
Jonathan Corbet
maintainerRe: [RFC PATCH 5/5] tracing: Add the osnoise tracer
Daniel Bristot de Oliveira <[email protected]> writes:
A quick nit:
Quoted 5 lines
Documentation/trace/osnoise_tracer.rst | 149 ++ include/linux/ftrace_irq.h | 16 + include/trace/events/osnoise.h | 141 ++ kernel/trace/Kconfig | 34 + kernel/trace/Makefile | 1 +
Quoted 8 lines
kernel/trace/trace.h | 9 +- kernel/trace/trace_entries.h | 27 + kernel/trace/trace_osnoise.c | 1714 ++++++++++++++++++++++++ kernel/trace/trace_output.c | 72 +- 9 files changed, 2159 insertions(+), 4 deletions(-) create mode 100644 Documentation/trace/osnoise_tracer.rst create mode 100644 include/trace/events/osnoise.h create mode 100644 kernel/trace/trace_osnoise.c
When you create a new RST file, you need to add it to an index.rst (or similar) file so that it gets incorporated into the docs build.
The document itself looks good on a quick read. If you're making another pass over it, you might consider reducing the ``markup noise`` a bit; we try to keep that to a minimum in the kernel docs. But otherwise thanks for writing it!
jon
Re: [RFC PATCH 5/5] tracing: Add the osnoise tracer
Quoted 17 lines · Jonathan Corbet
Daniel Bristot de Oliveira <[email protected]> writes:
A quick nit:
Quoted 5 lines
Documentation/trace/osnoise_tracer.rst | 149 ++ include/linux/ftrace_irq.h | 16 + include/trace/events/osnoise.h | 141 ++ kernel/trace/Kconfig | 34 + kernel/trace/Makefile | 1 +Quoted 8 lines
kernel/trace/trace.h | 9 +- kernel/trace/trace_entries.h | 27 + kernel/trace/trace_osnoise.c | 1714 ++++++++++++++++++++++++ kernel/trace/trace_output.c | 72 +- 9 files changed, 2159 insertions(+), 4 deletions(-) create mode 100644 Documentation/trace/osnoise_tracer.rst create mode 100644 include/trace/events/osnoise.h create mode 100644 kernel/trace/trace_osnoise.c
Quoted 2 lines
When you create a new RST file, you need to add it to an
index.rst(or similar) file so that it gets incorporated into the docs build.
ack!
Quoted 5 lines
The document itself looks good on a quick read. If you're making another pass over it, you might consider reducing the ``markup noise`` a bit; we try to keep that to a minimum in the kernel docs. But otherwise thanks for writing it!
Thanks for the review, Jon. I will reduce the `` markup (on this, and on some other docs that are about to come :-))
-- Daniel
Quoted 2 lines
jon
kernel test robot
build botRe: [RFC PATCH 5/5] tracing: Add the osnoise tracer
In reply to Daniel Bristot de Oliveira[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch]
Hi Daniel,
[FYI, it's a private test report for your RFC patch.]
[auto build test ERROR on tip/perf/core]
[also build test ERROR on linux/master linus/master v5.12-rc6] [cannot apply to trace/for-next next-20210408]
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Daniel-Bristot-de-Oliveira/hwlat-improvements-and-osnoise-tracer/20210408-221655 base: https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git cface0326a6c2ae5c8f47bd466f07624b3e348a7 config: s390-randconfig-c023-20210408 (attached as .config) compiler: s390-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/8fc23c7171dc536d3ce8c17fb66cfe6fcdbc5fb6
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Daniel-Bristot-de-Oliveira/hwlat-improvements-and-osnoise-tracer/20210408-221655 git checkout 8fc23c7171dc536d3ce8c17fb66cfe6fcdbc5fb6
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=s390
If you fix the issue, kindly add following tag as appropriate
All errors (new ones prefixed by >>):
s390-linux-ld: arch/s390/appldata/appldata_base.o: in function `appldata_generic_handler':
appldata_base.c:(.text+0x1d0): undefined reference to `sysctl_vals'
s390-linux-ld: kernel/trace/trace.o: in function `__update_max_tr':
trace.c:(.text+0x6c0e): undefined reference to `latency_fsnotify'
s390-linux-ld: kernel/trace/trace_hwlat.o: in function `kthread_fn':
>> trace_hwlat.c:(.text+0xc26): undefined reference to `latency_fsnotify'
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
Steven Rostedt
maintainerRe: [RFC PATCH 5/5] tracing: Add the osnoise tracer
In reply to Daniel Bristot de OliveiraQuoted 90 lines · On Thu, 8 Apr 2021 16:13:23 +0200 Daniel Bristot de Oliveira…
In the context of high-performance computing (HPC), the Operating System Noise (osnoise) refers to the interference experienced by an application due to activities inside the operating system. In the context of Linux, NMIs, IRQs, SoftIRQs, and any other system thread can cause noise to the system. Moreover, hardware-related jobs can also cause noise, for example, via SMIs.
hwlat_detectoris one of the tools used to identify the most complex source of noise: hardware noise.In a nutshell, the
hwlat_detectorcreates a thread that runs periodically for a given period. At the beginning of a period, the thread disables interrupt and starts sampling. While running, the hwlatd thread reads the time in a loop. As interrupts are disabled, threads, IRQs, and SoftIRQs cannot interfere with the hwlatd thread. Hence, the cause of any gap between two different reads of the time roots either on NMI or in the hardware itself. At the end of the period, hwlatd enables interrupts and reports the max observed gap between the reads. It also prints an NMI occurrence counter. If the output does not report NMI executions, the user can conclude that the hardware is the culprit for the latency. The hwlat detects the NMI execution by observing the entry and exit of an NMI.The osnoise tracer leverages the
hwlat_detectorby running a similar loop with preemption, SoftIRQs and IRQs enabled, thus allowing all the sources of osnoise during its execution. Using the same approach of hwlat, osnoise takes note of the entry and exit point of any source of interferences, increasing a per-cpu interference counter. The osnoise tracer also saves an interference counter for each source of interference. The interference counter for NMI, IRQs, SoftIRQs, and threads is increased anytime the tool observes these interferences' entry events. When a noise happens without any interference from the operating system level, the hardware noise counter increases, pointing to a hardware-related noise. In this way, osnoise can account for any source of interference. At the end of the period, the osnoise tracer prints the sum of all noise, the max single noise, the percentage of CPU available for the thread, and the counters for the noise sources.Usage
Write the ASCII text osnoise into the
current_tracerfile of the tracing system (generally mounted at/sys/kernel/tracingor/sys/kernel/debug/tracing).For example::
[root@f32 ~]# cd
/sys/kernel/tracing/[root@f32 tracing]# echo osnoise >current_tracerIt is possible to follow the trace by reading the trace trace file::
[root@f32 tracing]# cat trace # tracer: osnoise # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth MAX # || / SINGLE Interference counters: # |||| RUNTIME NOISE % OF CPU NOISE +-----------------------------+ # TASK-PID CPU# |||| TIMESTAMP IN US IN US AVAILABLE IN US HW NMI IRQ SIRQ THREAD # | | | |||| | | | | | | | | | | <...>-859 [000] .... 81.637220: 1000000 190 99.98100 9 18 0 1007 18 1 <...>-860 [001] .... 81.638154: 1000000 656 99.93440 74 23 0 1006 16 3 <...>-861 [002] .... 81.638193: 1000000 5675 99.43250 202 6 0 1013 25 21 <...>-862 [003] .... 81.638242: 1000000 125 99.98750 45 1 0 1011 23 0 <...>-863 [004] .... 81.638260: 1000000 1721 99.82790 168 7 0 1002 49 41 <...>-864 [005] .... 81.638286: 1000000 263 99.97370 57 6 0 1006 26 2 <...>-865 [006] .... 81.638302: 1000000 109 99.98910 21 3 0 1006 18 1 <...>-866 [007] .... 81.638326: 1000000 7816 99.21840 107 8 0 1016 39 19In addition to the regular trace fields (from TASK-PID to TIMESTAMP), the tracer prints a message at the end of each period for each CPU that is running an osnoise/ thread. The osnoise specific fields report:
- The RUNTIME IN USE reports the amount of time in microseconds that the osnoise thread kept looping reading the time.
- The NOISE IN US reports the sum of noise in microseconds observed by the osnoise tracer during the associated runtime.
- The % OF CPU AVAILABLE reports the percentage of CPU available for the osnoise thread during the runtime window.
- The MAX SINGLE NOISE IN US reports the maximum single noise observed during the runtime window.
- The Interference counters display how many each of the respective interference happened during the runtime window.Note that the example above shows a high number of HW noise samples. The reason being is that this sample was taken on a virtual machine, and the host interference is detected as a hardware interference.
Quoted 5 lines
Tracer options
The tracer has a set of options inside the osnoise directory, they are:
- cpus: CPUs at which a osnoise thread will execute.
Again, I think we can reuse the tracing_cpumask.
Quoted 2 lines
-
period_us: the period of the osnoise thread.
-runtime_us: how long an osnoise thread will look for noise.
These seem the same as window and width. At a minimum should probably share the same code.
Quoted 8 lines
-
stop_tracing_single_us: stop the system tracing of a single noise higher than the configured value is happens. Writing 0 disables this option.
-stop_tracing_total_us: stop the system tracing of a NOISE IN USE higher than the configured value is happens. Writing 0 disables this option.
-tolerance_ns: the minimum delta between twotime()reads to be considered as noise.
You can use tracing_threshold for the tolerance. Do you really need it in ns?
Quoted 31 lines
Additional Tracing
In addition to the tracer, a set of tracepoints were added to facilitate the identification of the osnoise source.
- osnoise:sample_threshold: printed anytime a noise is higher than the configurable
tolerance_ns.
- osnoise:nmi_noise: noise from NMI, including the duration.
- osnoise:irq_noise: noise from an IRQ, including the duration.
- osnoise:softirq_noise: noise from a SoftIRQ, including the duration.
- osnoise:thread_noise: noise from a thread, including the duration.Note that a all the values are net values. This means that a thread duration will not contain the duration of the IRQs that happened during its execution, for example. The same is valid for all duration values.
Here is one example of the usage of these tracepoints::
osnoise/8-961 [008] d.h. 5789.857532: irq_noise: local_timer:236 start 5789.857529929 duration 1845 ns osnoise/8-961 [008] dNh. 5789.858408: irq_noise: local_timer:236 start 5789.858404871 duration 2848 ns migration/8-54 [008] d... 5789.858413: thread_noise: migration/8:54 start 5789.858409300 duration 3068 ns osnoise/8-961 [008] .... 5789.858413: sample_threshold: start 5789.858404555 duration 8 us interferences 2In this example, a noise sample of 8 microseconds was reported in the last fine, pointing to two interferences. Looking backward in the trace, the two previous entries were about the migration thread running after a timer IRQ execution. The first event is not part of the noise because it took place one millisecond before.
Quoted 6 lines
It is worth noticing that the sum of the duration reported in the tracepoints is smaller than eight us reported in the
sample_threshold. The reason roots in the tracing overhead and in the overhead of the entry and exit code that happens before and after any interference execution. This justifies the dual approach: measuring thread and tracing.
I'm not sure the tracing overhead had much to do with it as did the overhead of entering the interrupt itself. events are rather fast (usually less than 200ns depending on the system). You can always enable the benchmark_event to see what trace event overhead is. Then again, cold cache can play into it as well.
Quoted 73 lines
Cc Jonathan Corbet <[email protected]>Cc Steven Rostedt <[email protected]>Cc Ingo Molnar <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Thomas Gleixner <[email protected]>Cc Alexandre Chartre <[email protected]>Cc Clark Willaims <[email protected]>Cc John Kacur <[email protected]>Cc Juri Lelli <[email protected]>Signed-off-by Daniel Bristot de Oliveira <[email protected]>--- Documentation/trace/osnoise_tracer.rst | 149 ++ include/linux/ftrace_irq.h | 16 + include/trace/events/osnoise.h | 141 ++ kernel/trace/Kconfig | 34 + kernel/trace/Makefile | 1 + kernel/trace/trace.h | 9 +- kernel/trace/trace_entries.h | 27 + kernel/trace/trace_osnoise.c | 1714 ++++++++++++++++++++++++ kernel/trace/trace_output.c | 72 +- 9 files changed, 2159 insertions(+), 4 deletions(-) create mode 100644 Documentation/trace/osnoise_tracer.rst create mode 100644 include/trace/events/osnoise.h create mode 100644 kernel/trace/trace_osnoise.cDocumentation/trace/osnoise_tracer.rst new file +38
-0@@ -0,0 +1,149 @@ +============== +OSNOISE Tracer +============== + +In the context of high-performance computing (HPC), the Operating System +Noise (*osnoise*) refers to the interference experienced by an application +due to activities inside the operating system. In the context of Linux, +NMIs, IRQs, SoftIRQs, and any other system thread can cause noise to the +system. Moreover, hardware-related jobs can also cause noise, for example, +via SMIs. + +``hwlat_detector`` is one of the tools used to identify the most complex +source of noise: *hardware noise*. + +In a nutshell, the ``hwlat_detector`` creates a thread that runs +periodically for a given period. At the beginning of a period, the thread +disables interrupt and starts sampling. While running, the ``hwlatd`` +thread reads the time in a loop. As interrupts are disabled, threads, +IRQs, and SoftIRQs cannot interfere with the ``hwlatd`` thread. Hence, the +cause of any gap between two different reads of the time roots either on +NMI or in the hardware itself. At the end of the period, ``hwlatd`` enables +interrupts and reports the max observed gap between the reads. It also +prints a NMI occurrence counter. If the output does not report NMI +executions, the user can conclude that the hardware is the culprit for +the latency. The ``hwlat`` detects the NMI execution by observing +the entry and exit of a NMI. + +The ``osnoise`` tracer leverages the ``hwlat_detector`` by running a +similar loop with preemption, SoftIRQs and IRQs enabled, thus allowing +all the sources of *osnoise* during its execution. Using the same approach +of ``hwlat``, ``osnoise`` takes note of the entry and exit point of any +source of interferences, increasing a per-cpu interference counter. The +``osnoise`` tracer also saves an interference counter for each source of +interference. The interference counter for NMI, IRQs, SoftIRQs, and +threads is increased anytime the tool observes these interferences' entry +events. When a noise happens without any interference from the operating +system level, the hardware noise counter increases, pointing to a +hardware-related noise. In this way, ``osnoise`` can account for any
Documentation/trace/osnoise_tracer.rst line 48
Quoted 10 lines
Documentation/trace/osnoise_tracer.rst new file +10
-0+source of interference. At the end of the period, the ``osnoise`` tracer +prints the sum of all noise, the max single noise, the percentage of CPU +available for the thread, and the counters for the noise sources. + +Usage +----- + +Write the ASCII text ``osnoise`` into the ``current_tracer`` file of the +tracing system (generally mounted at ``/sys/kernel/tracing`` or +``/sys/kernel/debug/tracing``).
I wouldn't even mention the /sys/kernel/debug/tracing path, I'm trying to deprecated that.
Quoted 69 lines
Documentation/trace/osnoise_tracer.rst new file +69
-0+ +For example:: + + [root@f32 ~]# cd /sys/kernel/tracing/ + [root@f32 tracing]# echo osnoise > current_tracer + +It is possible to follow the trace by reading the ``trace`` trace file:: + + [root@f32 tracing]# cat trace + # tracer: osnoise + # + # _-----=> irqs-off + # / _----=> need-resched + # | / _---=> hardirq/softirq + # || / _--=> preempt-depth MAX + # || / SINGLE Interference counters: + # |||| RUNTIME NOISE % OF CPU NOISE +-----------------------------+ + # TASK-PID CPU# |||| TIMESTAMP IN US IN US AVAILABLE IN US HW NMI IRQ SIRQ THREAD + # | | | |||| | | | | | | | | | | + <...>-859 [000] .... 81.637220: 1000000 190 99.98100 9 18 0 1007 18 1 + <...>-860 [001] .... 81.638154: 1000000 656 99.93440 74 23 0 1006 16 3 + <...>-861 [002] .... 81.638193: 1000000 5675 99.43250 202 6 0 1013 25 21 + <...>-862 [003] .... 81.638242: 1000000 125 99.98750 45 1 0 1011 23 0 + <...>-863 [004] .... 81.638260: 1000000 1721 99.82790 168 7 0 1002 49 41 + <...>-864 [005] .... 81.638286: 1000000 263 99.97370 57 6 0 1006 26 2 + <...>-865 [006] .... 81.638302: 1000000 109 99.98910 21 3 0 1006 18 1 + <...>-866 [007] .... 81.638326: 1000000 7816 99.21840 107 8 0 1016 39 19 + +In addition to the regular trace fields (from TASK-PID to TIMESTAMP), the +tracer prints a message at the end of each period for each CPU that is +running an ``osnoise/`` thread. The osnoise specific fields report: + + - The ``RUNTIME IN USE`` reports the amount of time in microseconds that + the ``osnoise`` thread kept looping reading the time. + - The ``NOISE IN US`` reports the sum of noise in microseconds observed + by the osnoise tracer during the associated runtime. + - The ``% OF CPU AVAILABLE`` reports the percentage of CPU available for + the ``osnoise`` thread during the ``runtime`` window. + - The ``MAX SINGLE NOISE IN US`` reports the maximum single noise observed + during the ``runtime`` window. + - The ``Interference counters`` display how many each of the respective + interference happened during the ``runtime`` window. + +Note that the example above shows a high number of ``HW noise`` samples. +The reason being is that this sample was taken on a virtual machine, +and the host interference is detected as a hardware interference. + +Tracer options +--------------------- + +The tracer has a set of options inside the ``osnoise`` directory, they are: + + - ``cpus``: CPUs at which a ``osnoise`` thread will execute. + - ``period_us``: the period of the ``osnoise`` thread. + - ``runtime_us``: how long an ``osnoise`` thread will look for noise. + - ``stop_tracing_single_us``: stop the system tracing of a single noise + higher than the configured value is happens. Writing ``0`` disables this + option. + - ``stop_tracing_total_us``: stop the system tracing of a ``NOISE IN USE`` + higher than the configured value is happens. Writing ``0`` disables this + option. + - ``tolerance_ns``: the minimum delta between two time() reads to be + considered as noise. + +Additional Tracing +------------------ + +In addition to the tracer, a set of ``tracepoints`` were added to +facilitate the identification of the osnoise source.
Documentation/trace/osnoise_tracer.rst line 127
Quoted 10 lines
Documentation/trace/osnoise_tracer.rst new file +10
-0+ + - ``osnoise:sample_threshold``: printed anytime a noise is higher than + the configurable ``tolerance_ns``. + - ``osnoise:nmi_noise``: noise from NMI, including the duration. + - ``osnoise:irq_noise``: noise from an IRQ, including the duration. + - ``osnoise:softirq_noise``: noise from a SoftIRQ, including the + duration. + - ``osnoise:thread_noise``: noise from a thread, including the duration. + +Note that a all the values are *net values*. This means that a *thread*
"a all"?
Documentation/trace/osnoise_tracer.rst line 129
Quoted 2 lines
Documentation/trace/osnoise_tracer.rst new file +2
-0+duration will not contain the duration of the *IRQs* that happened during +its execution, for example. The same is valid for all duration values.
The above is hard to understand. Do you mean individual instances of noise is not recorded, and only the sum is?
Documentation/trace/osnoise_tracer.rst line 139
Quoted 10 lines
Documentation/trace/osnoise_tracer.rst new file +10
-0+ +Here is one example of the usage of these ``tracepoints``:: + + osnoise/8-961 [008] d.h. 5789.857532: irq_noise: local_timer:236 start 5789.857529929 duration 1845 ns + osnoise/8-961 [008] dNh. 5789.858408: irq_noise: local_timer:236 start 5789.858404871 duration 2848 ns + migration/8-54 [008] d... 5789.858413: thread_noise: migration/8:54 start 5789.858409300 duration 3068 ns + osnoise/8-961 [008] .... 5789.858413: sample_threshold: start 5789.858404555 duration 8 us interferences 2 + +In this example, a noise sample of 8 microseconds was reported in the last +fine, pointing to two interferences. Looking backward in the trace, the
"fine"?
Quoted 15 lines
Documentation/trace/osnoise_tracer.rst new file +10
-0+two previous entries were about the ``migration`` thread running after +a timer IRQ execution. The first event is not part of the noise because +it took place one millisecond before. + +It is worth noticing that the sum of the duration reported in the +``tracepoints`` is smaller than eight us reported in the +``sample_threshold``. The reason roots in the tracing overhead and in +the overhead of the entry and exit code that happens before and after +any interference execution. This justifies the dual approach: measuring +thread and tracing.include/linux/ftrace_irq.h +0
-0
include/linux/ftrace_irq.h extern bool trace_hwlat_callback_enabled; · line 11
Quoted 5 lines
include/linux/ftrace_irq.h +2
-0@@ -7,12 +7,24 @@ extern bool trace_hwlat_callback_enabled;extern void trace_hwlat_callback (bool enter);#endif +/* +* XXX: Make it generic
Yes, this should be the same for both the hwlat detector and for osnoise.
Quoted 43 lines
include/linux/ftrace_irq.h +14
-0+*/ +#ifdef CONFIG_OSNOISE_TRACER +extern bool trace_osnoise_callback_enabled; +extern void trace_osnoise_callback (bool enter); +#endif +static inline void ftrace_nmi_enter (void ) {#ifdef CONFIG_HWLAT_TRACER if (trace_hwlat_callback_enabled)trace_hwlat_callback (true);#endif +#ifdef CONFIG_OSNOISE_TRACER +if (trace_osnoise_callback_enabled) +trace_osnoise_callback (true); +#endif }static inline void ftrace_nmi_exit (void )@@ -21,6 +33,10 @@ static inline void ftrace_nmi_exit(void)if (trace_hwlat_callback_enabled)trace_hwlat_callback (false);#endif +#ifdef CONFIG_OSNOISE_TRACER +if (trace_osnoise_callback_enabled) +trace_osnoise_callback (false); +#endif }#endif /* _LINUX_FTRACE_IRQ_H */ include/trace/events/osnoise.h new file +7
-0@@ -0,0 +1,141 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM osnoise + +#if !defined(_OSNOISE_TRACE_H) || defined(TRACE_HEADER_MULTI_READ ) +#define _OSNOISE_TRACE_H +
include/trace/events/osnoise.h line 17
Quoted 10 lines
include/trace/events/osnoise.h new file +10
-0+#include <linux/tracepoint.h> +TRACE_EVENT(thread_noise, + + TP_PROTO(struct task_struct *t, u64 start, u64 duration), + + TP_ARGS(t, start, duration), + + TP_STRUCT__entry( + __array( char, comm, TASK_COMM_LEN) + __field( pid_t, pid )
I would place the start and duration first. As pid is 4 bytes, you have a 4 byte "hole" in the structure:
system: osnoise
name: thread_noise
ID: 442
format:
field:unsigned short common_type; offset:0; size:2; signed:0; field:unsigned char common_flags; offset:2; size:1; signed:0; field:unsigned char common_preempt_count; offset:3; size:1; signed:0; field:int common_pid; offset:4; size:4; signed:1; field:char comm[16]; offset:8; size:16; signed:1; field:pid_t pid; offset:24; size:4; signed:1;
[ 4 bytes of nothing here ]
field:u64 start; offset:32; size:8; signed:0; field:u64 duration; offset:40; size:8; signed:0;
Quoted 19 lines
include/trace/events/osnoise.h new file +19
-0+ __field( u64, start ) +__field ( u64, duration) + ), + +TP_fast_assign ( + memcpy(__entry->comm, t->comm, TASK_COMM_LEN); + __entry->pid = t->pid; + __entry->start = start; + __entry->duration = duration; + ), + +TP_printk ("%8s:%d start %llu.%09u duration %llu ns" , + __entry->comm, + __entry->pid, +__print_ns_to_secs (__entry->start), +__print_ns_without_secs (__entry->start), + __entry->duration) +); +
include/trace/events/osnoise.h line 46
Quoted 10 lines
include/trace/events/osnoise.h new file +10
-0+TRACE_EVENT(softirq_noise, + + TP_PROTO(int vector, u64 start, u64 duration), + + TP_ARGS(vector, start, duration), + + TP_STRUCT__entry( + __field( int, vector ) + __field( u64, start ) + __field( u64, duration)
Same here.
name: softirq_noise
ID: 441
format:
field:unsigned short common_type; offset:0; size:2; signed:0; field:unsigned char common_flags; offset:2; size:1; signed:0; field:unsigned char common_preempt_count; offset:3; size:1; signed:0; field:int common_pid; offset:4; size:4; signed:1; field:int vector; offset:8; size:4; signed:1;
[ 4 bytes of nothing here]
field:u64 start; offset:16; size:8; signed:0; field:u64 duration; offset:24; size:8; signed:0;
Quoted 15 lines
include/trace/events/osnoise.h new file +15
-0+ ), + + TP_fast_assign( + __entry->vector = vector; + __entry->start = start; + __entry->duration = duration; + ), + +TP_printk ("%8s:%d start %llu.%09u duration %llu ns" , +show_softirq_name (__entry->vector), + __entry->vector, +__print_ns_to_secs (__entry->start), +__print_ns_without_secs (__entry->start), + __entry->duration) +);
include/trace/events/osnoise.h line 71
Quoted 10 lines
include/trace/events/osnoise.h new file +10
-0+ +TRACE_EVENT(irq_noise, + + TP_PROTO(int vector, const char *desc, u64 start, u64 duration), + + TP_ARGS(vector, desc, start, duration), + + TP_STRUCT__entry( + __string( desc, desc ) + __field( int, vector )
This doesn't have a hole, but I think it should still switch to be consistent.
Quoted 68 lines
include/trace/events/osnoise.h new file +68
-0+ __field( u64, start ) +__field ( u64, duration) + ), + +TP_fast_assign ( + __assign_str(desc, desc); + __entry->vector = vector; + __entry->start = start; + __entry->duration = duration; + ), + +TP_printk ("%s:%d start %llu.%09u duration %llu ns" , +__get_str (desc), + __entry->vector, +__print_ns_to_secs (__entry->start), +__print_ns_without_secs (__entry->start), + __entry->duration) +); + +TRACE_EVENT (nmi_noise, + + TP_PROTO(u64 start, u64 duration), + +TP_ARGS (start, duration), + +TP_STRUCT__entry ( + __field( u64, start ) +__field ( u64, duration) + ), + +TP_fast_assign ( + __entry->start = start; + __entry->duration = duration; + ), + +TP_printk ("start %llu.%09u duration %llu ns" , +__print_ns_to_secs (__entry->start), +__print_ns_without_secs (__entry->start), + __entry->duration) +); + +TRACE_EVENT (sample_threshold, + + TP_PROTO(u64 start, u64 duration, u64 interference), + +TP_ARGS (start, duration, interference), + +TP_STRUCT__entry ( + __field( u64, start ) + __field( u64, duration) +__field ( u64, interference) + ), + +TP_fast_assign ( + __entry->start = start; + __entry->duration = duration; + __entry->interference = interference; + ), + +TP_printk ("start %llu.%09u duration %llu us interferences %llu" , +__print_ns_to_secs (__entry->start), +__print_ns_without_secs (__entry->start), + __entry->duration, + __entry->interference) +); + +#endif /* _TRACE_OSNOISE_H */ +
[..]
include/trace/events/osnoise.h
Quoted 7 lines
include/trace/events/osnoise.h new file +7
-0+static void osnoise_tracer_start (struct trace_array *tr) +{ +int retval; + +/* Only allow one instance to enable this */ +if (osnoise_busy) +return ;
I found that I couldn't start this with:
trace-cmd start -B foo -p osnoise
Quoted 63 lines
include/trace/events/osnoise.h new file +63
-0+ +/* +* Trace is already hooked, we are re-enabling from +* a stop_tracing_*. +*/ +if (trace_osnoise_callback_enabled) +return ; + +osn_var_reset_all (); + + retval =hook_irq_events (); +if (retval) + goto err; + + retval =hook_softirq_events (); +if (retval) + goto out_unhook_irq; + + retval =hook_thread_events (); + +if (retval) + goto out_unrook_softirq; + +/* +* Make sure NMIs see reseted values. +*/ +barrier (); + trace_osnoise_callback_enabled = true; + + retval =start_per_cpu_kthreads (tr); +/* +* all fine! +*/ +if (!retval) +return ; + +unhook_thread_events (); +out_unrook_softirq: +unhook_softirq_events (); +out_unhook_irq: +unhook_irq_events (); +err: +pr_err (BANNER "Error starting osnoise tracer\n" ); +} + +static void osnoise_tracer_stop (struct trace_array *tr) +{ +/* Only allow one instance to enable this */ +if (!osnoise_busy) +return ; + + trace_osnoise_callback_enabled = false; +barrier (); + +stop_per_cpu_kthreads (); + +unhook_irq_events (); +unhook_softirq_events (); +unhook_thread_events (); +} + +static int osnoise_tracer_init (struct trace_array *tr) +{
include/trace/events/osnoise.h
Quoted 10 lines
include/trace/events/osnoise.h new file +10
-0+/* Only allow one instance to enable this */ +if (osnoise_busy) +return -EBUSY ; + + osnoise_trace = tr; + + tr->max_latency =0 ; + +if (tracer_tracing_is_on (tr)) +osnoise_tracer_start (tr);
That's because trace-cmd will disable tracing when it enables a tracer. And the above "osnoise_trace_start() is not called.
include/trace/events/osnoise.h
Quoted 2 lines
include/trace/events/osnoise.h new file +2
-0+ + osnoise_busy = true;
Once this is set, when we enable tracing, the start wont start.
-- Steve
Quoted 136 lines
include/trace/events/osnoise.h new file +39
-0+ + +return 0 ; +} + +static void osnoise_tracer_reset (struct trace_array *tr) +{ +osnoise_tracer_stop (tr); + + osnoise_busy = false; +} + +static struct tracer osnoise_tracer__read_mostly = { + .name ="osnoise" , + .init = osnoise_tracer_init, + .reset = osnoise_tracer_reset, + .start = osnoise_tracer_start, + .stop = osnoise_tracer_stop, + .print_header = print_osnoise_headers, + .allow_instances = true, +}; + +__init static int init_osnoise_tracer (void ) +{ +int ret; + +mutex_init (&osnoise_data.lock); + + ret =register_tracer (&osnoise_tracer); +if (ret) +return ret; + +cpumask_copy (&osnoise_cpumask, cpu_all_mask); + +init_tracefs (); + +return 0 ; +} +late_initcall (init_osnoise_tracer);kernel/trace/trace_output.c +71
-1@@ -1189,7 +1189,6 @@ trace_hwlat_print(struct trace_iterator *iter, int flags,return trace_handle_return (s); } -static enum print_line_t trace_hwlat_raw (struct trace_iterator *iter,int flags,struct trace_event *event)@@ -1219,6 +1218,76 @@ static struct trace_event trace_hwlat_event = { .funcs = &trace_hwlat_funcs, }; +/* TRACE_OSNOISE */ +static enum print_line_t +trace_osnoise_print (struct trace_iterator *iter,int flags, +struct trace_event *event) +{ +struct trace_entry *entry = iter->ent; +struct trace_seq *s = &iter->seq; +struct osnoise_entry *field; +u64 ratio, ratio_dec; +u64 net_runtime; + +trace_assign_type (field, entry); + +/* +* compute the available % of cpu time. +*/ + net_runtime = field->runtime - field->noise; + ratio = net_runtime *10000000 ; +do_div (ratio, field->runtime); + ratio_dec =do_div (ratio,100000 ); + +trace_seq_printf (s,"%llu %10llu %3llu.%05llu %7llu" , + field->runtime, + field->noise, + ratio, ratio_dec, + field->max_sample); + +trace_seq_printf (s," %6u" , field->hw_count); +trace_seq_printf (s," %6u" , field->nmi_count); +trace_seq_printf (s," %6u" , field->irq_count); +trace_seq_printf (s," %6u" , field->softirq_count); +trace_seq_printf (s," %6u" , field->thread_count); + +trace_seq_putc (s,'\n' ); + +return trace_handle_return (s); +} + +static enum print_line_t +trace_osnoise_raw (struct trace_iterator *iter,int flags, +struct trace_event *event) +{ +struct osnoise_entry *field; +struct trace_seq *s = &iter->seq; + +trace_assign_type (field, iter->ent); + +trace_seq_printf (s,"%lld %llu %llu %u %u %u %u %u\n" , + field->runtime, + field->noise, + field->max_sample, + field->hw_count, + field->nmi_count, + field->irq_count, + field->softirq_count, + field->thread_count); + +return trace_handle_return (s); +} + +static struct trace_event_functions trace_osnoise_funcs = { + .trace = trace_osnoise_print, + .raw = trace_osnoise_raw, +}; + +static struct trace_event trace_osnoise_event = { + .type =TRACE_OSNOISE , + .funcs = &trace_osnoise_funcs, +}; +/* TRACE_BPUTS */ static enum print_line_t trace_bputs_print(struct trace_iterator *iter, int flags,@@ -1384,6 +1453,7 @@ static struct trace_event *events[] __initdata = { &trace_bprint_event, &trace_print_event, &trace_hwlat_event, + &trace_osnoise_event, &trace_raw_data_event, NULL };
Re: [RFC PATCH 5/5] tracing: Add the osnoise tracer
Quoted 99 lines · Steven Rostedt
Quoted 90 lines · On Thu, 8 Apr 2021 16:13:23 +0200 Daniel Bristot de Oliveira…
In the context of high-performance computing (HPC), the Operating System Noise (osnoise) refers to the interference experienced by an application due to activities inside the operating system. In the context of Linux, NMIs, IRQs, SoftIRQs, and any other system thread can cause noise to the system. Moreover, hardware-related jobs can also cause noise, for example, via SMIs.
hwlat_detectoris one of the tools used to identify the most complex source of noise: hardware noise.In a nutshell, the
hwlat_detectorcreates a thread that runs periodically for a given period. At the beginning of a period, the thread disables interrupt and starts sampling. While running, the hwlatd thread reads the time in a loop. As interrupts are disabled, threads, IRQs, and SoftIRQs cannot interfere with the hwlatd thread. Hence, the cause of any gap between two different reads of the time roots either on NMI or in the hardware itself. At the end of the period, hwlatd enables interrupts and reports the max observed gap between the reads. It also prints an NMI occurrence counter. If the output does not report NMI executions, the user can conclude that the hardware is the culprit for the latency. The hwlat detects the NMI execution by observing the entry and exit of an NMI.The osnoise tracer leverages the
hwlat_detectorby running a similar loop with preemption, SoftIRQs and IRQs enabled, thus allowing all the sources of osnoise during its execution. Using the same approach of hwlat, osnoise takes note of the entry and exit point of any source of interferences, increasing a per-cpu interference counter. The osnoise tracer also saves an interference counter for each source of interference. The interference counter for NMI, IRQs, SoftIRQs, and threads is increased anytime the tool observes these interferences' entry events. When a noise happens without any interference from the operating system level, the hardware noise counter increases, pointing to a hardware-related noise. In this way, osnoise can account for any source of interference. At the end of the period, the osnoise tracer prints the sum of all noise, the max single noise, the percentage of CPU available for the thread, and the counters for the noise sources.Usage
Write the ASCII text osnoise into the
current_tracerfile of the tracing system (generally mounted at/sys/kernel/tracingor/sys/kernel/debug/tracing).For example::
[root@f32 ~]# cd
/sys/kernel/tracing/[root@f32 tracing]# echo osnoise >current_tracerIt is possible to follow the trace by reading the trace trace file::
[root@f32 tracing]# cat trace # tracer: osnoise # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth MAX # || / SINGLE Interference counters: # |||| RUNTIME NOISE % OF CPU NOISE +-----------------------------+ # TASK-PID CPU# |||| TIMESTAMP IN US IN US AVAILABLE IN US HW NMI IRQ SIRQ THREAD # | | | |||| | | | | | | | | | | <...>-859 [000] .... 81.637220: 1000000 190 99.98100 9 18 0 1007 18 1 <...>-860 [001] .... 81.638154: 1000000 656 99.93440 74 23 0 1006 16 3 <...>-861 [002] .... 81.638193: 1000000 5675 99.43250 202 6 0 1013 25 21 <...>-862 [003] .... 81.638242: 1000000 125 99.98750 45 1 0 1011 23 0 <...>-863 [004] .... 81.638260: 1000000 1721 99.82790 168 7 0 1002 49 41 <...>-864 [005] .... 81.638286: 1000000 263 99.97370 57 6 0 1006 26 2 <...>-865 [006] .... 81.638302: 1000000 109 99.98910 21 3 0 1006 18 1 <...>-866 [007] .... 81.638326: 1000000 7816 99.21840 107 8 0 1016 39 19In addition to the regular trace fields (from TASK-PID to TIMESTAMP), the tracer prints a message at the end of each period for each CPU that is running an osnoise/ thread. The osnoise specific fields report:
- The RUNTIME IN USE reports the amount of time in microseconds that the osnoise thread kept looping reading the time.
- The NOISE IN US reports the sum of noise in microseconds observed by the osnoise tracer during the associated runtime.
- The % OF CPU AVAILABLE reports the percentage of CPU available for the osnoise thread during the runtime window.
- The MAX SINGLE NOISE IN US reports the maximum single noise observed during the runtime window.
- The Interference counters display how many each of the respective interference happened during the runtime window.Note that the example above shows a high number of HW noise samples. The reason being is that this sample was taken on a virtual machine, and the host interference is detected as a hardware interference.
Quoted 5 lines
Tracer options
The tracer has a set of options inside the osnoise directory, they are:
- cpus: CPUs at which a osnoise thread will execute.
Quoted 7 lines
Again, I think we can reuse the
tracing_cpumask.Quoted 2 lines
-
period_us: the period of the osnoise thread.
-runtime_us: how long an osnoise thread will look for noise.These seem the same as window and width. At a minimum should probably share the same code.
how about creating a generic handler for all the "to long" writes, that receives a structure containing a pointer to where to save the value, a min and a max acceptable value?
If so, where to place this function? trace.c?
Quoted 11 lines
Quoted 8 lines
-
stop_tracing_single_us: stop the system tracing of a single noise higher than the configured value is happens. Writing 0 disables this option.
-stop_tracing_total_us: stop the system tracing of a NOISE IN USE higher than the configured value is happens. Writing 0 disables this option.
-tolerance_ns: the minimum delta between twotime()reads to be considered as noise.You can use
tracing_thresholdfor the tolerance. Do you really need it in ns?
Yes, I can. I placed it in ns to serve as a fine tune that one might need. But, I can use the tracing_threshold in us as well.
Quoted 38 lines
Quoted 31 lines
Additional Tracing
In addition to the tracer, a set of tracepoints were added to facilitate the identification of the osnoise source.
- osnoise:sample_threshold: printed anytime a noise is higher than the configurable
tolerance_ns.
- osnoise:nmi_noise: noise from NMI, including the duration.
- osnoise:irq_noise: noise from an IRQ, including the duration.
- osnoise:softirq_noise: noise from a SoftIRQ, including the duration.
- osnoise:thread_noise: noise from a thread, including the duration.Note that a all the values are net values. This means that a thread duration will not contain the duration of the IRQs that happened during its execution, for example. The same is valid for all duration values.
Here is one example of the usage of these tracepoints::
osnoise/8-961 [008] d.h. 5789.857532: irq_noise: local_timer:236 start 5789.857529929 duration 1845 ns osnoise/8-961 [008] dNh. 5789.858408: irq_noise: local_timer:236 start 5789.858404871 duration 2848 ns migration/8-54 [008] d... 5789.858413: thread_noise: migration/8:54 start 5789.858409300 duration 3068 ns osnoise/8-961 [008] .... 5789.858413: sample_threshold: start 5789.858404555 duration 8 us interferences 2In this example, a noise sample of 8 microseconds was reported in the last fine, pointing to two interferences. Looking backward in the trace, the two previous entries were about the migration thread running after a timer IRQ execution. The first event is not part of the noise because it took place one millisecond before.
Quoted 6 lines
It is worth noticing that the sum of the duration reported in the tracepoints is smaller than eight us reported in the
sample_threshold. The reason roots in the tracing overhead and in the overhead of the entry and exit code that happens before and after any interference execution. This justifies the dual approach: measuring thread and tracing.
Quoted 5 lines
I'm not sure the tracing overhead had much to do with it as did the overhead of entering the interrupt itself. events are rather fast (usually less than 200ns depending on the system). You can always enable the
benchmark_eventto see what trace event overhead is. Then again, cold cache can play into it as well.
I agree, I will remove the tracing overhead part.
Quoted 78 lines
Quoted 78 lines
Cc Jonathan Corbet <[email protected]>Cc Steven Rostedt <[email protected]>Cc Ingo Molnar <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Thomas Gleixner <[email protected]>Cc Alexandre Chartre <[email protected]>Cc Clark Willaims <[email protected]>Cc John Kacur <[email protected]>Cc Juri Lelli <[email protected]>Signed-off-by Daniel Bristot de Oliveira <[email protected]>--- Documentation/trace/osnoise_tracer.rst | 149 ++ include/linux/ftrace_irq.h | 16 + include/trace/events/osnoise.h | 141 ++ kernel/trace/Kconfig | 34 + kernel/trace/Makefile | 1 + kernel/trace/trace.h | 9 +- kernel/trace/trace_entries.h | 27 + kernel/trace/trace_osnoise.c | 1714 ++++++++++++++++++++++++ kernel/trace/trace_output.c | 72 +- 9 files changed, 2159 insertions(+), 4 deletions(-) create mode 100644 Documentation/trace/osnoise_tracer.rst create mode 100644 include/trace/events/osnoise.h create mode 100644 kernel/trace/trace_osnoise.cDocumentation/trace/osnoise_tracer.rst new file +43
-0@@ -0,0 +1,149 @@ +============== +OSNOISE Tracer +============== + +In the context of high-performance computing (HPC), the Operating System +Noise (*osnoise*) refers to the interference experienced by an application +due to activities inside the operating system. In the context of Linux, +NMIs, IRQs, SoftIRQs, and any other system thread can cause noise to the +system. Moreover, hardware-related jobs can also cause noise, for example, +via SMIs. + +``hwlat_detector`` is one of the tools used to identify the most complex +source of noise: *hardware noise*. + +In a nutshell, the ``hwlat_detector`` creates a thread that runs +periodically for a given period. At the beginning of a period, the thread +disables interrupt and starts sampling. While running, the ``hwlatd`` +thread reads the time in a loop. As interrupts are disabled, threads, +IRQs, and SoftIRQs cannot interfere with the ``hwlatd`` thread. Hence, the +cause of any gap between two different reads of the time roots either on +NMI or in the hardware itself. At the end of the period, ``hwlatd`` enables +interrupts and reports the max observed gap between the reads. It also +prints a NMI occurrence counter. If the output does not report NMI +executions, the user can conclude that the hardware is the culprit for +the latency. The ``hwlat`` detects the NMI execution by observing +the entry and exit of a NMI. + +The ``osnoise`` tracer leverages the ``hwlat_detector`` by running a +similar loop with preemption, SoftIRQs and IRQs enabled, thus allowing +all the sources of *osnoise* during its execution. Using the same approach +of ``hwlat``, ``osnoise`` takes note of the entry and exit point of any +source of interferences, increasing a per-cpu interference counter. The +``osnoise`` tracer also saves an interference counter for each source of +interference. The interference counter for NMI, IRQs, SoftIRQs, and +threads is increased anytime the tool observes these interferences' entry +events. When a noise happens without any interference from the operating +system level, the hardware noise counter increases, pointing to a +hardware-related noise. In this way, ``osnoise`` can account for any +source of interference. At the end of the period, the ``osnoise`` tracer +prints the sum of all noise, the max single noise, the percentage of CPU +available for the thread, and the counters for the noise sources. + +Usage
Quoted 8 lines
Documentation/trace/osnoise_tracer.rst line 48
Quoted 5 lines
Documentation/trace/osnoise_tracer.rst new file +5
-0+----- + +Write the ASCII text ``osnoise`` into the ``current_tracer`` file of the +tracing system (generally mounted at ``/sys/kernel/tracing`` or +``/sys/kernel/debug/tracing``).I wouldn't even mention the
/sys/kernel/debug/tracingpath, I'm trying to deprecated that.
I mention it because that is the path (still) used on Fedora...
Quoted 73 lines
Quoted 73 lines
Documentation/trace/osnoise_tracer.rst new file +73
-0+ +For example:: + + [root@f32 ~]# cd /sys/kernel/tracing/ + [root@f32 tracing]# echo osnoise > current_tracer + +It is possible to follow the trace by reading the ``trace`` trace file:: + + [root@f32 tracing]# cat trace + # tracer: osnoise + # + # _-----=> irqs-off + # / _----=> need-resched + # | / _---=> hardirq/softirq + # || / _--=> preempt-depth MAX + # || / SINGLE Interference counters: + # |||| RUNTIME NOISE % OF CPU NOISE +-----------------------------+ + # TASK-PID CPU# |||| TIMESTAMP IN US IN US AVAILABLE IN US HW NMI IRQ SIRQ THREAD + # | | | |||| | | | | | | | | | | + <...>-859 [000] .... 81.637220: 1000000 190 99.98100 9 18 0 1007 18 1 + <...>-860 [001] .... 81.638154: 1000000 656 99.93440 74 23 0 1006 16 3 + <...>-861 [002] .... 81.638193: 1000000 5675 99.43250 202 6 0 1013 25 21 + <...>-862 [003] .... 81.638242: 1000000 125 99.98750 45 1 0 1011 23 0 + <...>-863 [004] .... 81.638260: 1000000 1721 99.82790 168 7 0 1002 49 41 + <...>-864 [005] .... 81.638286: 1000000 263 99.97370 57 6 0 1006 26 2 + <...>-865 [006] .... 81.638302: 1000000 109 99.98910 21 3 0 1006 18 1 + <...>-866 [007] .... 81.638326: 1000000 7816 99.21840 107 8 0 1016 39 19 + +In addition to the regular trace fields (from TASK-PID to TIMESTAMP), the +tracer prints a message at the end of each period for each CPU that is +running an ``osnoise/`` thread. The osnoise specific fields report: + + - The ``RUNTIME IN USE`` reports the amount of time in microseconds that + the ``osnoise`` thread kept looping reading the time. + - The ``NOISE IN US`` reports the sum of noise in microseconds observed + by the osnoise tracer during the associated runtime. + - The ``% OF CPU AVAILABLE`` reports the percentage of CPU available for + the ``osnoise`` thread during the ``runtime`` window. + - The ``MAX SINGLE NOISE IN US`` reports the maximum single noise observed + during the ``runtime`` window. + - The ``Interference counters`` display how many each of the respective + interference happened during the ``runtime`` window. + +Note that the example above shows a high number of ``HW noise`` samples. +The reason being is that this sample was taken on a virtual machine, +and the host interference is detected as a hardware interference. + +Tracer options +--------------------- + +The tracer has a set of options inside the ``osnoise`` directory, they are: + + - ``cpus``: CPUs at which a ``osnoise`` thread will execute. + - ``period_us``: the period of the ``osnoise`` thread. + - ``runtime_us``: how long an ``osnoise`` thread will look for noise. + - ``stop_tracing_single_us``: stop the system tracing of a single noise + higher than the configured value is happens. Writing ``0`` disables this + option. + - ``stop_tracing_total_us``: stop the system tracing of a ``NOISE IN USE`` + higher than the configured value is happens. Writing ``0`` disables this + option. + - ``tolerance_ns``: the minimum delta between two time() reads to be + considered as noise. + +Additional Tracing +------------------ + +In addition to the tracer, a set of ``tracepoints`` were added to +facilitate the identification of the osnoise source. + + - ``osnoise:sample_threshold``: printed anytime a noise is higher than + the configurable ``tolerance_ns``. + - ``osnoise:nmi_noise``: noise from NMI, including the duration.
Quoted 8 lines
Documentation/trace/osnoise_tracer.rst line 127
Quoted 6 lines
Documentation/trace/osnoise_tracer.rst new file +6
-0+ - ``osnoise:irq_noise``: noise from an IRQ, including the duration. + - ``osnoise:softirq_noise``: noise from a SoftIRQ, including the + duration. + - ``osnoise:thread_noise``: noise from a thread, including the duration. + +Note that a all the values are *net values*. This means that a *thread*"a all"?
Oops, I will fix that.
Quoted 5 lines
Documentation/trace/osnoise_tracer.rst line 129
Quoted 2 lines
Documentation/trace/osnoise_tracer.rst new file +2
-0+duration will not contain the duration of the *IRQs* that happened during +its execution, for example. The same is valid for all duration values.The above is hard to understand. Do you mean individual instances of noise is not recorded, and only the sum is?
I need to rephrase that.... I meant that when we have two or more "noise" stacked, e.g., a thread noise happening, and then an IRQ noise happens on top of it, the noise from the TOP will be discounted, e.g., the IRQ noise added to the thread noise will be discounted. Like...
osnoise_running
--------> thread noise in
run 5 us
----------------> IRQ noise
run 3 us
<---------------- print duration 3 us
run 1 us
<--------- print duration 6 us (not 9 us).Making this computation in kernel reduces the amount of events printed in the buffer.
Quoted 4 lines
Quoted 4 lines
Documentation/trace/osnoise_tracer.rst new file +4
-0+ +Here is one example of the usage of these ``tracepoints``:: + + osnoise/8-961 [008] d.h. 5789.857532: irq_noise: local_timer:236 start 5789.857529929 duration 1845 ns
Quoted 8 lines
Documentation/trace/osnoise_tracer.rst line 139
Quoted 6 lines
Documentation/trace/osnoise_tracer.rst new file +6
-0+ osnoise/8-961 [008] dNh. 5789.858408: irq_noise: local_timer:236 start 5789.858404871 duration 2848 ns + migration/8-54 [008] d... 5789.858413: thread_noise: migration/8:54 start 5789.858409300 duration 3068 ns + osnoise/8-961 [008] .... 5789.858413: sample_threshold: start 5789.858404555 duration 8 us interferences 2 + +In this example, a noise sample of 8 microseconds was reported in the last +fine, pointing to two interferences. Looking backward in the trace, the"fine"?
Ops, "line" :-). Will fix.
Quoted 18 lines
Quoted 18 lines
Documentation/trace/osnoise_tracer.rst new file +10
-0+two previous entries were about the ``migration`` thread running after +a timer IRQ execution. The first event is not part of the noise because +it took place one millisecond before. + +It is worth noticing that the sum of the duration reported in the +``tracepoints`` is smaller than eight us reported in the +``sample_threshold``. The reason roots in the tracing overhead and in +the overhead of the entry and exit code that happens before and after +any interference execution. This justifies the dual approach: measuring +thread and tracing.include/linux/ftrace_irq.h +0
-0@@ -7,12 +7,24 @@ extern bool trace_hwlat_callback_enabled;extern void trace_hwlat_callback (bool enter);#endif
Quoted 5 lines
include/linux/ftrace_irq.h extern bool trace_hwlat_callback_enabled; · line 11
Quoted 2 lines
include/linux/ftrace_irq.h +2
-0+/* +* XXX: Make it generic Yes, this should be the same for both the hwlat detector and for osnoise.
Where should I place it? On hwlat, making osnoise to select it? On trace.c?
Quoted 69 lines
Quoted 43 lines
include/linux/ftrace_irq.h +14
-0+*/ +#ifdef CONFIG_OSNOISE_TRACER +extern bool trace_osnoise_callback_enabled; +extern void trace_osnoise_callback (bool enter); +#endif +static inline void ftrace_nmi_enter (void ) {#ifdef CONFIG_HWLAT_TRACER if (trace_hwlat_callback_enabled)trace_hwlat_callback (true);#endif +#ifdef CONFIG_OSNOISE_TRACER +if (trace_osnoise_callback_enabled) +trace_osnoise_callback (true); +#endif }static inline void ftrace_nmi_exit (void )@@ -21,6 +33,10 @@ static inline void ftrace_nmi_exit(void)if (trace_hwlat_callback_enabled)trace_hwlat_callback (false);#endif +#ifdef CONFIG_OSNOISE_TRACER +if (trace_osnoise_callback_enabled) +trace_osnoise_callback (false); +#endif }#endif /* _LINUX_FTRACE_IRQ_H */ include/trace/events/osnoise.h new file +7
-0@@ -0,0 +1,141 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM osnoise + +#if !defined(_OSNOISE_TRACE_H) || defined(TRACE_HEADER_MULTI_READ ) +#define _OSNOISE_TRACE_H +include/trace/events/osnoise.h line 17
Quoted 10 lines
include/trace/events/osnoise.h new file +10
-0+#include <linux/tracepoint.h> +TRACE_EVENT(thread_noise, + + TP_PROTO(struct task_struct *t, u64 start, u64 duration), + + TP_ARGS(t, start, duration), + + TP_STRUCT__entry( + __array( char, comm, TASK_COMM_LEN) + __field( pid_t, pid )I would place the start and duration first. As pid is 4 bytes, you have a 4 byte "hole" in the structure:
system: osnoise
name:thread_noise
ID: 442
format:field:unsigned short common_type; offset:0; size:2; signed:0; field:unsigned char common_flags; offset:2; size:1; signed:0; field:unsigned char common_preempt_count; offset:3; size:1; signed:0; field:int common_pid; offset:4; size:4; signed:1; field:char comm[16]; offset:8; size:16; signed:1; field:pid_t pid; offset:24; size:4; signed:1;
Quoted 5 lines
[ 4 bytes of nothing here ]
field:u64 start; offset:32; size:8; signed:0; field:u64 duration; offset:40; size:8; signed:0;
Ack, will fix that.
Quoted 42 lines
Quoted 19 lines
include/trace/events/osnoise.h new file +19
-0+ __field( u64, start ) +__field ( u64, duration) + ), + +TP_fast_assign ( + memcpy(__entry->comm, t->comm, TASK_COMM_LEN); + __entry->pid = t->pid; + __entry->start = start; + __entry->duration = duration; + ), + +TP_printk ("%8s:%d start %llu.%09u duration %llu ns" , + __entry->comm, + __entry->pid, +__print_ns_to_secs (__entry->start), +__print_ns_without_secs (__entry->start), + __entry->duration) +); +include/trace/events/osnoise.h line 46
Quoted 10 lines
include/trace/events/osnoise.h new file +10
-0+TRACE_EVENT(softirq_noise, + + TP_PROTO(int vector, u64 start, u64 duration), + + TP_ARGS(vector, start, duration), + + TP_STRUCT__entry( + __field( int, vector ) + __field( u64, start ) + __field( u64, duration)Same here.
name:
softirq_noise
ID: 441
format:field:unsigned short common_type; offset:0; size:2; signed:0; field:unsigned char common_flags; offset:2; size:1; signed:0; field:unsigned char common_preempt_count; offset:3; size:1; signed:0; field:int common_pid; offset:4; size:4; signed:1; field:int vector; offset:8; size:4; signed:1;
Quoted 4 lines
[ 4 bytes of nothing here]
field:u64 start; offset:16; size:8; signed:0; field:u64 duration; offset:24; size:8; signed:0;
ack!
Quoted 20 lines
Quoted 20 lines
include/trace/events/osnoise.h new file +20
-0+ ), + + TP_fast_assign( + __entry->vector = vector; + __entry->start = start; + __entry->duration = duration; + ), + +TP_printk ("%8s:%d start %llu.%09u duration %llu ns" , +show_softirq_name (__entry->vector), + __entry->vector, +__print_ns_to_secs (__entry->start), +__print_ns_without_secs (__entry->start), + __entry->duration) +); + +TRACE_EVENT(irq_noise, + + TP_PROTO(int vector, const char *desc, u64 start, u64 duration), +
Quoted 8 lines
include/trace/events/osnoise.h line 71
Quoted 5 lines
include/trace/events/osnoise.h new file +5
-0+TP_ARGS (vector, desc, start, duration), + +TP_STRUCT__entry ( + __string( desc, desc ) + __field( int, vector )This doesn't have a hole, but I think it should still switch to be consistent.
Ack!
Quoted 83 lines
Quoted 69 lines
include/trace/events/osnoise.h new file +68
-0+ __field( u64, start ) +__field ( u64, duration) + ), + +TP_fast_assign ( + __assign_str(desc, desc); + __entry->vector = vector; + __entry->start = start; + __entry->duration = duration; + ), + +TP_printk ("%s:%d start %llu.%09u duration %llu ns" , +__get_str (desc), + __entry->vector, +__print_ns_to_secs (__entry->start), +__print_ns_without_secs (__entry->start), + __entry->duration) +); + +TRACE_EVENT (nmi_noise, + + TP_PROTO(u64 start, u64 duration), + +TP_ARGS (start, duration), + +TP_STRUCT__entry ( + __field( u64, start ) +__field ( u64, duration) + ), + +TP_fast_assign ( + __entry->start = start; + __entry->duration = duration; + ), + +TP_printk ("start %llu.%09u duration %llu ns" , +__print_ns_to_secs (__entry->start), +__print_ns_without_secs (__entry->start), + __entry->duration) +); + +TRACE_EVENT (sample_threshold, + + TP_PROTO(u64 start, u64 duration, u64 interference), + +TP_ARGS (start, duration, interference), + +TP_STRUCT__entry ( + __field( u64, start ) + __field( u64, duration) +__field ( u64, interference) + ), + +TP_fast_assign ( + __entry->start = start; + __entry->duration = duration; + __entry->interference = interference; + ), + +TP_printk ("start %llu.%09u duration %llu us interferences %llu" , +__print_ns_to_secs (__entry->start), +__print_ns_without_secs (__entry->start), + __entry->duration, + __entry->interference) +); + +#endif /* _TRACE_OSNOISE_H */ +[..]
include/trace/events/osnoise.h
Quoted 7 lines
include/trace/events/osnoise.h new file +7
-0+static void osnoise_tracer_start (struct trace_array *tr) +{ +int retval; + +/* Only allow one instance to enable this */ +if (osnoise_busy) +return ;I found that I couldn't start this with:
Quoted 1 line
trace-cmd start -B foo -p osnoise
ok, I will debug that.
Quoted 77 lines
Quoted 63 lines
include/trace/events/osnoise.h new file +63
-0+ +/* +* Trace is already hooked, we are re-enabling from +* a stop_tracing_*. +*/ +if (trace_osnoise_callback_enabled) +return ; + +osn_var_reset_all (); + + retval =hook_irq_events (); +if (retval) + goto err; + + retval =hook_softirq_events (); +if (retval) + goto out_unhook_irq; + + retval =hook_thread_events (); + +if (retval) + goto out_unrook_softirq; + +/* +* Make sure NMIs see reseted values. +*/ +barrier (); + trace_osnoise_callback_enabled = true; + + retval =start_per_cpu_kthreads (tr); +/* +* all fine! +*/ +if (!retval) +return ; + +unhook_thread_events (); +out_unrook_softirq: +unhook_softirq_events (); +out_unhook_irq: +unhook_irq_events (); +err: +pr_err (BANNER "Error starting osnoise tracer\n" ); +} + +static void osnoise_tracer_stop (struct trace_array *tr) +{ +/* Only allow one instance to enable this */ +if (!osnoise_busy) +return ; + + trace_osnoise_callback_enabled = false; +barrier (); + +stop_per_cpu_kthreads (); + +unhook_irq_events (); +unhook_softirq_events (); +unhook_thread_events (); +} + +static int osnoise_tracer_init (struct trace_array *tr) +{include/trace/events/osnoise.h
Quoted 10 lines
include/trace/events/osnoise.h new file +10
-0+/* Only allow one instance to enable this */ +if (osnoise_busy) +return -EBUSY ; + + osnoise_trace = tr; + + tr->max_latency =0 ; + +if (tracer_tracing_is_on (tr)) +osnoise_tracer_start (tr);That's because trace-cmd will disable tracing when it enables a tracer. And the above "
osnoise_trace_start()is not called.
Quoted 4 lines
include/trace/events/osnoise.h
Quoted 2 lines
include/trace/events/osnoise.h new file +2
-0+ + osnoise_busy = true;Once this is set, when we enable tracing, the start wont start.
Ok, I will try to understand this better.
Thanks
-- Daniel
Quoted 142 lines
-- Steve
Quoted 136 lines
include/trace/events/osnoise.h new file +39
-0+ + +return 0 ; +} + +static void osnoise_tracer_reset (struct trace_array *tr) +{ +osnoise_tracer_stop (tr); + + osnoise_busy = false; +} + +static struct tracer osnoise_tracer__read_mostly = { + .name ="osnoise" , + .init = osnoise_tracer_init, + .reset = osnoise_tracer_reset, + .start = osnoise_tracer_start, + .stop = osnoise_tracer_stop, + .print_header = print_osnoise_headers, + .allow_instances = true, +}; + +__init static int init_osnoise_tracer (void ) +{ +int ret; + +mutex_init (&osnoise_data.lock); + + ret =register_tracer (&osnoise_tracer); +if (ret) +return ret; + +cpumask_copy (&osnoise_cpumask, cpu_all_mask); + +init_tracefs (); + +return 0 ; +} +late_initcall (init_osnoise_tracer);kernel/trace/trace_output.c +71
-1@@ -1189,7 +1189,6 @@ trace_hwlat_print(struct trace_iterator *iter, int flags,return trace_handle_return (s); } -static enum print_line_t trace_hwlat_raw (struct trace_iterator *iter,int flags,struct trace_event *event)@@ -1219,6 +1218,76 @@ static struct trace_event trace_hwlat_event = { .funcs = &trace_hwlat_funcs, }; +/* TRACE_OSNOISE */ +static enum print_line_t +trace_osnoise_print (struct trace_iterator *iter,int flags, +struct trace_event *event) +{ +struct trace_entry *entry = iter->ent; +struct trace_seq *s = &iter->seq; +struct osnoise_entry *field; +u64 ratio, ratio_dec; +u64 net_runtime; + +trace_assign_type (field, entry); + +/* +* compute the available % of cpu time. +*/ + net_runtime = field->runtime - field->noise; + ratio = net_runtime *10000000 ; +do_div (ratio, field->runtime); + ratio_dec =do_div (ratio,100000 ); + +trace_seq_printf (s,"%llu %10llu %3llu.%05llu %7llu" , + field->runtime, + field->noise, + ratio, ratio_dec, + field->max_sample); + +trace_seq_printf (s," %6u" , field->hw_count); +trace_seq_printf (s," %6u" , field->nmi_count); +trace_seq_printf (s," %6u" , field->irq_count); +trace_seq_printf (s," %6u" , field->softirq_count); +trace_seq_printf (s," %6u" , field->thread_count); + +trace_seq_putc (s,'\n' ); + +return trace_handle_return (s); +} + +static enum print_line_t +trace_osnoise_raw (struct trace_iterator *iter,int flags, +struct trace_event *event) +{ +struct osnoise_entry *field; +struct trace_seq *s = &iter->seq; + +trace_assign_type (field, iter->ent); + +trace_seq_printf (s,"%lld %llu %llu %u %u %u %u %u\n" , + field->runtime, + field->noise, + field->max_sample, + field->hw_count, + field->nmi_count, + field->irq_count, + field->softirq_count, + field->thread_count); + +return trace_handle_return (s); +} + +static struct trace_event_functions trace_osnoise_funcs = { + .trace = trace_osnoise_print, + .raw = trace_osnoise_raw, +}; + +static struct trace_event trace_osnoise_event = { + .type =TRACE_OSNOISE , + .funcs = &trace_osnoise_funcs, +}; +/* TRACE_BPUTS */ static enum print_line_t trace_bputs_print(struct trace_iterator *iter, int flags,@@ -1384,6 +1453,7 @@ static struct trace_event *events[] __initdata = { &trace_bprint_event, &trace_print_event, &trace_hwlat_event, + &trace_osnoise_event, &trace_raw_data_event, NULL };