* [LTP] Testing absence of ticks with nohz_full
@ 2014-03-10 13:50 Mats Liljegren
2014-03-10 14:04 ` chrubis
` (2 more replies)
0 siblings, 3 replies; 28+ messages in thread
From: Mats Liljegren @ 2014-03-10 13:50 UTC (permalink / raw)
To: Linux Test Project General Discussions; +Cc: Frederic Weisbecker, Kevin Hilman
My company (Enea) wants to create test case(s) that verifies that you
can run an application without having ticks going on the CPU that the
application runs on. This would utilize the new nohz_full feature
available from kernel version 3.10.
I cannot find any existing test case in LTP in this area, so I assume I
have to create the test case myself.
One catch is that the current nohz_full implementation actually
generates 1Hz. So part of the test could be to verify that we actually
get 1Hz rather than full tick speed. It would also be possible to
support the experimental patch from Kevin Hilman to actually turn ticks
off for real. The test code code check whether the patch is applied and
based on it expect 0Hz or 1Hz ticks.
Would LTP be a good place for such tests?
Regards
Mats Liljegren
------------------------------------------------------------------------------
Learn Graph Databases - Download FREE O'Reilly Book
"Graph Databases" is the definitive new guide to graph databases and their
applications. Written by three acclaimed leaders in the field,
this first edition is now available. Download your free book today!
http://p.sf.net/sfu/13534_NeoTech
_______________________________________________
Ltp-list mailing list
Ltp-list@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/ltp-list
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [LTP] Testing absence of ticks with nohz_full 2014-03-10 13:50 [LTP] Testing absence of ticks with nohz_full Mats Liljegren @ 2014-03-10 14:04 ` chrubis 2014-03-10 15:17 ` Frederic Weisbecker 2014-03-13 22:10 ` Kevin Hilman 2 siblings, 0 replies; 28+ messages in thread From: chrubis @ 2014-03-10 14:04 UTC (permalink / raw) To: Mats Liljegren Cc: Linux Test Project General Discussions, Kevin Hilman, Frederic Weisbecker Hi! > My company (Enea) wants to create test case(s) that verifies that you > can run an application without having ticks going on the CPU that the > application runs on. This would utilize the new nohz_full feature > available from kernel version 3.10. > > I cannot find any existing test case in LTP in this area, so I assume I > have to create the test case myself. I'm not aware of such tests in LTP. > One catch is that the current nohz_full implementation actually > generates 1Hz. So part of the test could be to verify that we actually > get 1Hz rather than full tick speed. It would also be possible to > support the experimental patch from Kevin Hilman to actually turn ticks > off for real. The test code code check whether the patch is applied and > based on it expect 0Hz or 1Hz ticks. > > Would LTP be a good place for such tests? Certainly. I've wrote (about two months ago) a short guide on writing LTP testcases and contributing the code have look at: https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines And if you have any questions don't hesitate to ask on LTP mailing list. -- Cyril Hrubis chrubis@suse.cz ------------------------------------------------------------------------------ Learn Graph Databases - Download FREE O'Reilly Book "Graph Databases" is the definitive new guide to graph databases and their applications. Written by three acclaimed leaders in the field, this first edition is now available. Download your free book today! http://p.sf.net/sfu/13534_NeoTech _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [LTP] Testing absence of ticks with nohz_full 2014-03-10 13:50 [LTP] Testing absence of ticks with nohz_full Mats Liljegren 2014-03-10 14:04 ` chrubis @ 2014-03-10 15:17 ` Frederic Weisbecker 2014-03-10 15:27 ` Steven Rostedt 2014-03-11 10:34 ` Mats Liljegren 2014-03-13 22:10 ` Kevin Hilman 2 siblings, 2 replies; 28+ messages in thread From: Frederic Weisbecker @ 2014-03-10 15:17 UTC (permalink / raw) To: Mats Liljegren Cc: Arnaldo Carvalho de Melo, Linux Test Project General Discussions, Kevin Hilman, Namhyung Kim, Steven Rostedt, Jiri Olsa, Ingo Molnar On Mon, Mar 10, 2014 at 02:50:16PM +0100, Mats Liljegren wrote: > My company (Enea) wants to create test case(s) that verifies that > you can run an application without having ticks going on the CPU > that the application runs on. This would utilize the new nohz_full > feature available from kernel version 3.10. Ah nice! > > I cannot find any existing test case in LTP in this area, so I > assume I have to create the test case myself. > > One catch is that the current nohz_full implementation actually > generates 1Hz. So part of the test could be to verify that we > actually get 1Hz rather than full tick speed. It would also be > possible to support the experimental patch from Kevin Hilman to > actually turn ticks off for real. The test code code check whether > the patch is applied and based on it expect 0Hz or 1Hz ticks. > > Would LTP be a good place for such tests? If that can be of any help, you can have a look at this: https://git.kernel.org/cgit/linux/kernel/git/frederic/dynticks-testing.git/ It's selftest that setup some isolation configuration, run a userspace loop for a few seconds and records various trace events like timers, workqueue, etc... So it's not automatically telling if a CPU runs full dynticks, some analysis from a user on the traces is required. Now it's fairly possible to write a small test that tells if the CPU noise is reduced to strict 1 Hz timer and nothing else. But that alone wouldn't provide safe guarantees because workqueues of whatever timer may happen anytime. In really depends on your usecase. Do you want your CPU not be disturbed in order to maximize its throughput? That's the HPC case. There you can cope with a few noise from time to time. Then such a test would be relevant. Now if you have latency requirement, which means having a tick or workqueue or anything disturbing you CPU would screw up everything then you're rather on the side of real time and then you need guarantee. For such a case I'd rather suggest live monitoring of trace events. So there is two possibilities: 1) Show that the CPU runs in full dynticks for a given timeslice. 2) Monitoring events through the whole timeslices. On both cases you want to use trace events. So I'd rather suggest you to use perf tools or trace-cmd. I believe that trace-cmd unfortunately uses per CPU periodic wake up on live recording CPUs so perhaps it's not a good candidate to test isolation. Too bad because kernelshark is cool for an overview. Also perf supports some script languages for post processing: python, perl, etc... I think trace-cmd too but rather for custom display of events. Adding a few more people in Cc. > > Regards > Mats Liljegren ------------------------------------------------------------------------------ Learn Graph Databases - Download FREE O'Reilly Book "Graph Databases" is the definitive new guide to graph databases and their applications. Written by three acclaimed leaders in the field, this first edition is now available. Download your free book today! http://p.sf.net/sfu/13534_NeoTech _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [LTP] Testing absence of ticks with nohz_full 2014-03-10 15:17 ` Frederic Weisbecker @ 2014-03-10 15:27 ` Steven Rostedt 2014-03-10 15:33 ` Frederic Weisbecker 2014-03-11 10:34 ` Mats Liljegren 1 sibling, 1 reply; 28+ messages in thread From: Steven Rostedt @ 2014-03-10 15:27 UTC (permalink / raw) To: Frederic Weisbecker Cc: Melo, Discussions, Arnaldo, Kevin Hilman, Namhyung Kim, Linux, Darren Hart, Johannes Berg, Jiri Olsa, Ingo Molnar On Mon, 10 Mar 2014 16:17:48 +0100 Frederic Weisbecker <fweisbec@gmail.com> wrote: > On both cases you want to use trace events. So I'd rather suggest you to use perf tools > or trace-cmd. > > I believe that trace-cmd unfortunately uses per CPU periodic wake up on live > recording CPUs so perhaps it's not a good candidate to test isolation. Too bad > because kernelshark is cool for an overview. Actually, it's not per-cpu. There's a thread per-cpu, but they do not need to run on the CPU they monitor. Heck, you can have all threads on just one CPU. Actually, they are not threads, they are forked children, but you get the idea. Now that I have wakeup working (with help of irq_work), I should also be able to have them only wake if there's something to trace. > > Also perf supports some script languages for post processing: python, perl, etc... > I think trace-cmd too but rather for custom display of events. Yeah, you can write python plugins that will change the display of the output. I'm not sure about full integrating with python though. > > Adding a few more people in Cc. Me too. -- Steve ------------------------------------------------------------------------------ Learn Graph Databases - Download FREE O'Reilly Book "Graph Databases" is the definitive new guide to graph databases and their applications. Written by three acclaimed leaders in the field, this first edition is now available. Download your free book today! http://p.sf.net/sfu/13534_NeoTech _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [LTP] Testing absence of ticks with nohz_full 2014-03-10 15:27 ` Steven Rostedt @ 2014-03-10 15:33 ` Frederic Weisbecker 0 siblings, 0 replies; 28+ messages in thread From: Frederic Weisbecker @ 2014-03-10 15:33 UTC (permalink / raw) To: Steven Rostedt Cc: Arnaldo Carvalho de Melo, Linux Test Project General Discussions, Kevin Hilman, Namhyung Kim, Darren Hart, Johannes Berg, Jiri Olsa, Ingo Molnar On Mon, Mar 10, 2014 at 11:27:56AM -0400, Steven Rostedt wrote: > On Mon, 10 Mar 2014 16:17:48 +0100 > Frederic Weisbecker <fweisbec@gmail.com> wrote: > > > > On both cases you want to use trace events. So I'd rather suggest you to use perf tools > > or trace-cmd. > > > > I believe that trace-cmd unfortunately uses per CPU periodic wake up on live > > recording CPUs so perhaps it's not a good candidate to test isolation. Too bad > > because kernelshark is cool for an overview. > > Actually, it's not per-cpu. There's a thread per-cpu, but they do not > need to run on the CPU they monitor. Heck, you can have all threads on > just one CPU. Actually, they are not threads, they are forked children, > but you get the idea. Ah good to know. > > Now that I have wakeup working (with help of irq_work), I should also > be able to have them only wake if there's something to trace. Perfect! > > > > > Also perf supports some script languages for post processing: python, perl, etc... > > I think trace-cmd too but rather for custom display of events. > > Yeah, you can write python plugins that will change the display of the > output. I'm not sure about full integrating with python though. Ok. So to produce a summary of events with a script maybe perf can do better. But kernelshark can offer a good overview as well. So this all depends on what Mats wants to do in the first place: have a tool which tells you about the noise, which, how much, etc... So post analysis. Or have a more visual summary. Thanks. ------------------------------------------------------------------------------ Learn Graph Databases - Download FREE O'Reilly Book "Graph Databases" is the definitive new guide to graph databases and their applications. Written by three acclaimed leaders in the field, this first edition is now available. Download your free book today! http://p.sf.net/sfu/13534_NeoTech _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [LTP] Testing absence of ticks with nohz_full 2014-03-10 15:17 ` Frederic Weisbecker 2014-03-10 15:27 ` Steven Rostedt @ 2014-03-11 10:34 ` Mats Liljegren 2014-03-11 23:31 ` Steven Rostedt 1 sibling, 1 reply; 28+ messages in thread From: Mats Liljegren @ 2014-03-11 10:34 UTC (permalink / raw) To: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo, Linux Test Project General Discussions, Kevin Hilman, Namhyung Kim, Steven Rostedt, Jiri Olsa, Ingo Molnar On Mon, Mar 10, 2014 at 4:17 PM, Frederic Weisbecker <fweisbec@gmail.com> wrote: > On Mon, Mar 10, 2014 at 02:50:16PM +0100, Mats Liljegren wrote: >> My company (Enea) wants to create test case(s) that verifies that >> you can run an application without having ticks going on the CPU >> that the application runs on. This would utilize the new nohz_full >> feature available from kernel version 3.10. > > Ah nice! > >> >> I cannot find any existing test case in LTP in this area, so I >> assume I have to create the test case myself. >> >> One catch is that the current nohz_full implementation actually >> generates 1Hz. So part of the test could be to verify that we >> actually get 1Hz rather than full tick speed. It would also be >> possible to support the experimental patch from Kevin Hilman to >> actually turn ticks off for real. The test code code check whether >> the patch is applied and based on it expect 0Hz or 1Hz ticks. >> >> Would LTP be a good place for such tests? > > If that can be of any help, you can have a look at this: > https://git.kernel.org/cgit/linux/kernel/git/frederic/dynticks-testing.git/ I'll check that and see if I get some inspiration. Thanks! > It's selftest that setup some isolation configuration, run a userspace loop for > a few seconds and records various trace events like timers, workqueue, etc... > > So it's not automatically telling if a CPU runs full dynticks, some analysis from > a user on the traces is required. > > Now it's fairly possible to write a small test that tells if the CPU noise > is reduced to strict 1 Hz timer and nothing else. But that alone wouldn't > provide safe guarantees because workqueues of whatever timer may happen > anytime. My naive first attempt would be something in this area. Count the ticks that occurred, compare with the expected number of ticks for the duration of the test, and report PASS/FAIL. Not sure how to handle queued up work though. Running long enough might be running for longer than anybody wants to wait for the result... > In really depends on your usecase. Do you want your CPU not be disturbed in > order to maximize its throughput? That's the HPC case. There you can cope with a few > noise from time to time. Then such a test would be relevant. Working for a company doing RTOS products, I'd say we're more for the real-time aspects. What kind of real-time is another, but very interesting, question. > Now if you have latency requirement, which means having a tick or workqueue or > anything disturbing you CPU would screw up everything then you're rather > on the side of real time and then you need guarantee. For such a case > I'd rather suggest live monitoring of trace events. > > So there is two possibilities: > > 1) Show that the CPU runs in full dynticks for a given timeslice. > > 2) Monitoring events through the whole timeslices. > > On both cases you want to use trace events. So I'd rather suggest you to use perf tools > or trace-cmd. I have given the "correct" way of counting ticks some thought, but I haven't come to a good conclusion. My current best bet is using function tracing on scheduler_tick(). It seems to be the most configuration and architecture independent way. But I'm not entirely sure it really is just one call for each tick. If you have better suggestions, I'd love to hear about it. > I believe that trace-cmd unfortunately uses per CPU periodic wake up on live > recording CPUs so perhaps it's not a good candidate to test isolation. Too bad > because kernelshark is cool for an overview. I've invented a "count_ticks" script which hides ftrace complexity on other interesting ways than trace_cmd does. It logs calls to scheduler_tick during a command execution, and goes through this log afterwards counting the calls. > Also perf supports some script languages for post processing: python, perl, etc... > I think trace-cmd too but rather for custom display of events. While debugging is nice too, my first step is to just get something that can say PASS/FAIL, with some information about why it failed (e.g. because x ticks occurred that was not expected). Nothing more fancy than that. Regards Mats Liljegren ------------------------------------------------------------------------------ Learn Graph Databases - Download FREE O'Reilly Book "Graph Databases" is the definitive new guide to graph databases and their applications. Written by three acclaimed leaders in the field, this first edition is now available. Download your free book today! http://p.sf.net/sfu/13534_NeoTech _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [LTP] Testing absence of ticks with nohz_full 2014-03-11 10:34 ` Mats Liljegren @ 2014-03-11 23:31 ` Steven Rostedt 0 siblings, 0 replies; 28+ messages in thread From: Steven Rostedt @ 2014-03-11 23:31 UTC (permalink / raw) To: Mats Liljegren Cc: Melo, Discussions, Arnaldo, Kevin Hilman, Frederic Weisbecker, Namhyung Kim, Linux, Jiri Olsa, Ingo Molnar On Tue, 11 Mar 2014 11:34:18 +0100 Mats Liljegren <liljegren.mats2@gmail.com> wrote: > I have given the "correct" way of counting ticks some thought, but I > haven't come to a good conclusion. My current best bet is using > function tracing on scheduler_tick(). It seems to be the most > configuration and architecture independent way. But I'm not entirely > sure it really is just one call for each tick. If you have better > suggestions, I'd love to hear about it. Try this: echo 1 > /sys/kernel/debug/tracing/max_graph_depth echo function_graph > /sys/kernel/debug/tracing/current_tracer If CPU 5 is the one running with nohz, then monitor: /sys/kernel/debug/tracing/per_cpu/cpu5/trace_pipe And see what goes into the kernel. The "max_graph_depth" is the max depth the function_graph tracer should trace. By saying "1", you are telling it to give you the first function it sees, but ignore all the rest. I specifically implemented this because I thought it would be a great way to see what interrupts user tasks. Now if the user task does a syscall, you will only see that and not any tick that happened when the syscall was running. But you don't care about that anyway ;-) > > > I believe that trace-cmd unfortunately uses per CPU periodic wake up on live > > recording CPUs so perhaps it's not a good candidate to test isolation. Too bad > > because kernelshark is cool for an overview. > > I've invented a "count_ticks" script which hides ftrace complexity on > other interesting ways than trace_cmd does. It logs calls to > scheduler_tick during a command execution, and goes through this log > afterwards counting the calls. > > > Also perf supports some script languages for post processing: python, perl, etc... > > I think trace-cmd too but rather for custom display of events. > > While debugging is nice too, my first step is to just get something > that can say PASS/FAIL, with some information about why it failed > (e.g. because x ticks occurred that was not expected). Nothing more > fancy than that. I have scripts that test things like this. Not this exactly. All they are are bash scripts that read the trace file output and does simple parsing to see what happened during the test. And at the end, it simply says "PASS" or "FAIL". -- Steve ------------------------------------------------------------------------------ Learn Graph Databases - Download FREE O'Reilly Book "Graph Databases" is the definitive new guide to graph databases and their applications. Written by three acclaimed leaders in the field, this first edition is now available. Download your free book today! http://p.sf.net/sfu/13534_NeoTech _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [LTP] Testing absence of ticks with nohz_full 2014-03-10 13:50 [LTP] Testing absence of ticks with nohz_full Mats Liljegren 2014-03-10 14:04 ` chrubis 2014-03-10 15:17 ` Frederic Weisbecker @ 2014-03-13 22:10 ` Kevin Hilman 2014-03-17 16:35 ` Mats Liljegren 2 siblings, 1 reply; 28+ messages in thread From: Kevin Hilman @ 2014-03-13 22:10 UTC (permalink / raw) To: Mats Liljegren Cc: Linux Test Project General Discussions, Frederic Weisbecker Mats Liljegren <liljegren.mats2@gmail.com> writes: > My company (Enea) wants to create test case(s) that verifies that you > can run an application without having ticks going on the CPU that the > application runs on. This would utilize the new nohz_full feature > available from kernel version 3.10. > > I cannot find any existing test case in LTP in this area, so I assume > I have to create the test case myself. It's not in LTP, but I have a shell script[1] does some setup and load generation that I use to validate nohz_full. The setup and prerequisites can be a bit tricky, but some of the things it does: - disables the residual 1Hz (if my patch is applied) - pinning the unbound "writeback" workqueue to CPU0 - force timer migration using hotplug - sets up CPUsets and tries to migrate all tasks to CPU0 (I think this part came from a script from you awhile ago) It then uses 'stress' to generate load on specific CPUs and uses trace-cmd to record the traces. I currently look at the traces manually to see if it worked, but adding other step to automatially look at the traces would be a good next step. Kevin [1] #!/bin/bash # Check that we have cpusets enabled in the kernel if ! grep -q -s cpuset /proc/filesystems ; then echo "Error: Kernel is lacking support for cpuset!" exit 1 fi # Try to disable sched_tick_max_deferment if [ -e /sys/kernel/debug/sched_tick_max_deferment ]; then echo -1 > /sys/kernel/debug/sched_tick_max_deferment else echo "WARNING: unable to set sched_tick_max_deferment" fi # if CONFIG_LOCKUP_DETECTOR is enabled, a periodic timer fires # on *every* CPU, including idle ones. Disable it. if [ -e /proc/sys/kernel/watchdog ]; then echo 0 > /proc/sys/kernel/watchdog fi declare -a all_cpus=(`for file in /sys/devices/system/cpu/cpu*/online; do basename $(dirname $file); done`) declare -a cpus=( ${all_cpus[@]/cpu0/} ) # without CPU0 max_cpu=$((${#all_cpus[@]} - 1)) # # Force migration off of NO_HZ CPUs using hotplug # TODO: document what happens without this # for cpu in ${cpus[@]}; do echo 0 > /sys/devices/system/cpu/$cpu/online done for cpu in ${cpus[@]}; do echo 1 > /sys/devices/system/cpu/$cpu/online done # TODO: document what happens without this # pin the writeback workqueue to CPU0 echo 1 > /sys/bus/workqueue/devices/writeback/cpumask # make sure that the /dev/cpuset dir exits # and mount the cpuset filesystem if needed [ -d /dev/cpuset ] || mkdir /dev/cpuset [ -e /dev/cpuset/tasks ] || mount -t cpuset none /dev/cpuset # Create 2 cpusets. One GP and one NOHZ domain. [ -d /dev/cpuset/gp ] || mkdir /dev/cpuset/gp [ -d /dev/cpuset/rt ] || mkdir /dev/cpuset/rt # Assume a single memory node echo 0 > /dev/cpuset/gp/mems echo 0 > /dev/cpuset/rt/mems # Setup the GP domain: CPU0 echo 0 > /dev/cpuset/gp/cpus # Setup the NOHZ domain: CPU1+ echo 1-$max_cpu > /dev/cpuset/rt/cpus # Try to move all processes in top set to the GP set. for pid in `cat /dev/cpuset/tasks`; do if [ -d /proc/$pid ]; then echo $pid > /dev/cpuset/gp/tasks 2>/dev/null if [ $? != 0 ]; then echo -n "Cannot move PID $pid: " echo "$(cat /proc/$pid/status | grep ^Name | cut -f2)" fi fi done # Disable load balancing on top level (otherwise the child-sets' setting # won't take effect.) echo 0 > /dev/cpuset/sched_load_balance # Enable load balancing withing the GP domain echo 1 > /dev/cpuset/gp/sched_load_balance #echo 1 > /dev/cpuset/rt/sched_load_balance # But disallow load balancing within the NOHZ domain echo 0 > /dev/cpuset/rt/sched_load_balance DURATION=30 BACKOFF=$((DURATION / 8 * 1000000)) # Move self into GP domain echo $$ > /dev/cpuset/gp/tasks trace-cmd start -b 10000 -e sched -e irq -e timer -e signal #trace-cmd start -b 100000 -e all # GP domain stress -q --cpu 2 --vm 2 --timeout $DURATION & GP_PID=$! # NOHZ domain echo $$ > /dev/cpuset/rt/tasks || exit 1 # 2 temporarly overlapping tasks stress -q --cpu 1 --timeout $[DURATION / 3] & stress -q --cpu 1 --backoff $BACKOFF --timeout $[DURATION - 1] & NOHZ_PID=$! # start another busy task on the last CPU for >2 CPU machines if [ $max_cpu > 1 ]; then taskset -c $max_cpu stress -q --cpu 1 --timeout $DURATION & fi # Switch back to GP echo $$ > /dev/cpuset/gp/tasks # Wait for GP task(s) to finish wait $GP_PID trace-cmd stop trace-cmd extract sync exit 0 # # Cleanup # # Try to move all from GP back to root for pid in `cat /dev/cpuset/gp/tasks`; do if [ -d /proc/$pid ]; then echo $pid > /dev/cpuset/tasks 2>/dev/null if [ $? != 0 ]; then echo -n "Cannot move PID $pid: " echo "$(cat /proc/$pid/status | grep ^Name | cut -f2)" fi fi done # Remove the CPUsets rmdir /dev/cpuset/gp rmdir /dev/cpuset/rt ------------------------------------------------------------------------------ Learn Graph Databases - Download FREE O'Reilly Book "Graph Databases" is the definitive new guide to graph databases and their applications. Written by three acclaimed leaders in the field, this first edition is now available. Download your free book today! http://p.sf.net/sfu/13534_NeoTech _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [LTP] Testing absence of ticks with nohz_full 2014-03-13 22:10 ` Kevin Hilman @ 2014-03-17 16:35 ` Mats Liljegren 2014-04-16 15:48 ` [LTP] [RFC][PATCH] partrt_nohz_full: Introducing a new test case Mats Liljegren 0 siblings, 1 reply; 28+ messages in thread From: Mats Liljegren @ 2014-03-17 16:35 UTC (permalink / raw) To: Kevin Hilman, Mats Liljegren Cc: Linux Test Project General Discussions, Frederic Weisbecker Thanks for the script! I was thinking about using stress as load both on the non-isolated CPUs as well as for the isolated CPUs. I intend to use "--io" and "--vm" for non-isolated part, and "--cpu" for the isolated. This should provoke the system a little bit at least. Regards Mats Liljegren ________________________________________ From: Kevin Hilman [khilman@linaro.org] Sent: Thursday, March 13, 2014 11:10 PM To: Mats Liljegren Cc: Linux Test Project General Discussions; Frederic Weisbecker Subject: Re: [LTP] Testing absence of ticks with nohz_full Mats Liljegren <liljegren.mats2@gmail.com> writes: > My company (Enea) wants to create test case(s) that verifies that you > can run an application without having ticks going on the CPU that the > application runs on. This would utilize the new nohz_full feature > available from kernel version 3.10. > > I cannot find any existing test case in LTP in this area, so I assume > I have to create the test case myself. It's not in LTP, but I have a shell script[1] does some setup and load generation that I use to validate nohz_full. The setup and prerequisites can be a bit tricky, but some of the things it does: - disables the residual 1Hz (if my patch is applied) - pinning the unbound "writeback" workqueue to CPU0 - force timer migration using hotplug - sets up CPUsets and tries to migrate all tasks to CPU0 (I think this part came from a script from you awhile ago) It then uses 'stress' to generate load on specific CPUs and uses trace-cmd to record the traces. I currently look at the traces manually to see if it worked, but adding other step to automatially look at the traces would be a good next step. Kevin ------------------------------------------------------------------------------ Learn Graph Databases - Download FREE O'Reilly Book "Graph Databases" is the definitive new guide to graph databases and their applications. Written by three acclaimed leaders in the field, this first edition is now available. Download your free book today! http://p.sf.net/sfu/13534_NeoTech _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
* [LTP] [RFC][PATCH] partrt_nohz_full: Introducing a new test case 2014-03-17 16:35 ` Mats Liljegren @ 2014-04-16 15:48 ` Mats Liljegren 2014-04-16 15:48 ` Mats Liljegren ` (2 more replies) 0 siblings, 3 replies; 28+ messages in thread From: Mats Liljegren @ 2014-04-16 15:48 UTC (permalink / raw) To: ltp-list; +Cc: Frederic Weisbecker, Kevin Hilman This patch introduces a new test case, partrt_nohz_full. This is sent as an RFC since as far as I understand it ltp is under stabilization. But I still wanted to send what I've got currently to see if there are any major issues with it. This test case requires a couple of tools which can be cloned from here: https://github.com/OpenEneaLinux/rt-tools The tools are scripts so they are easily copied to somewhere PATH will find them. The patch applies on ltp, which can be cloned from here: https://github.com/linux-test-project/ltp.git The patch applies to commit hash b24ce8f6bcf315078bdc48bbddf780b964ffa59a. To run the test case, do the following: partrt create <cpumask> runltp -f partrt_nohz_full where <cpumask> should be a hexadecimal mask of nohz_full kernel boot parameter. If you're too lazy to translate it by hand, you can use list2mask tool from the rt-tools repository: partrt create 0x$(list2mask --nohz) runltp -f partrt_nohz_full Regards Mats Liljegren ------------------------------------------------------------------------------ Learn Graph Databases - Download FREE O'Reilly Book "Graph Databases" is the definitive new guide to graph databases and their applications. Written by three acclaimed leaders in the field, this first edition is now available. Download your free book today! http://p.sf.net/sfu/NeoTech _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
* [LTP] [RFC][PATCH] partrt_nohz_full: Introducing a new test case 2014-04-16 15:48 ` [LTP] [RFC][PATCH] partrt_nohz_full: Introducing a new test case Mats Liljegren @ 2014-04-16 15:48 ` Mats Liljegren 2014-04-22 15:47 ` chrubis 2014-04-22 14:07 ` chrubis 2014-04-28 15:06 ` [LTP] [PATCH v2] " Mats Liljegren 2 siblings, 1 reply; 28+ messages in thread From: Mats Liljegren @ 2014-04-16 15:48 UTC (permalink / raw) To: ltp-list; +Cc: Frederic Weisbecker, Kevin Hilman This test case is intended to test the nohz_full kernel feature. See file testcases/kernel/partrt/README for more information. Signed-off-by: Mats Liljegren <mats.liljegren@enea.com> --- runtest/partrt_nohz_full | 1 + testcases/kernel/Makefile | 1 + testcases/kernel/partrt/Makefile | 24 + testcases/kernel/partrt/README | 65 ++ testcases/kernel/partrt/nohz_full/.gitignore | 1 + testcases/kernel/partrt/nohz_full/Makefile | 29 + .../partrt/nohz_full/test_partrt_nohz_full.c | 636 ++++++++++++++++++++ 7 files changed, 757 insertions(+) create mode 100644 runtest/partrt_nohz_full create mode 100644 testcases/kernel/partrt/Makefile create mode 100644 testcases/kernel/partrt/README create mode 100644 testcases/kernel/partrt/nohz_full/.gitignore create mode 100644 testcases/kernel/partrt/nohz_full/Makefile create mode 100644 testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c diff --git a/runtest/partrt_nohz_full b/runtest/partrt_nohz_full new file mode 100644 index 0000000..1f17569 --- /dev/null +++ b/runtest/partrt_nohz_full @@ -0,0 +1 @@ +partrt_nohz_full test_partrt_nohz_full --duration=3000 --verbose diff --git a/testcases/kernel/Makefile b/testcases/kernel/Makefile index 6bffe79..cd867a9 100644 --- a/testcases/kernel/Makefile +++ b/testcases/kernel/Makefile @@ -54,6 +54,7 @@ SUBDIRS += connectors \ timers \ tracing \ module \ + partrt \ ifeq ($(WITH_POWER_MANAGEMENT_TESTSUITE),yes) SUBDIRS += power_management diff --git a/testcases/kernel/partrt/Makefile b/testcases/kernel/partrt/Makefile new file mode 100644 index 0000000..f083a29 --- /dev/null +++ b/testcases/kernel/partrt/Makefile @@ -0,0 +1,24 @@ +# +# kernel/tick test suite Makefile. +# +# Copyright (C) 2014, Enea AB. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +top_srcdir ?= ../../.. + +include $(top_srcdir)/include/mk/env_pre.mk +include $(top_srcdir)/include/mk/generic_trunk_target.mk diff --git a/testcases/kernel/partrt/README b/testcases/kernel/partrt/README new file mode 100644 index 0000000..7ac9551 --- /dev/null +++ b/testcases/kernel/partrt/README @@ -0,0 +1,65 @@ +partrt_nohz_full +================ + +This directory contains part_nohz_full test case for verifying absence of ticks +on nohz_full enabled CPUs. For more information about nohz_full and how to +enable this feature, see the following document in the kernel source code: + + Documentation/timers/NO_HZ.txt + +CPU partitioning is a way of isolating work with real-time requirements from +work without real-time requirements by assuring that the real-time work is done +on dedicated CPUs. This uses the kernel feature cgroup/cpuset. The goal is to +make sure that nohz_full mode can be enabled on these CPUs. + +Dependencies +------------ +CPU partitioning is performed by the partrt tool, which is available as a part +of the rt-tools suite here: + + https://github.com/OpenEneaLinux/rt-tools + +The following build time kernel configurations must be enabled: + + CONFIG_NO_HZ_FULL=y + CONFIG_CPUSETS=y + +The following kernel boot parameter needs to be set: + + nohz_full=<cpu list> + +There might be other parameters needed to actually get nohz_full working, but +this is largely dependent on architecture and kernel version. + +How to run +---------- + +The partrt_nohz_full test is run by runltp like so: + + partrt create $(list2mask --nohz) + ./runltp -f partrt_nohz_full + partrt undo + +The "partrt create" will create the CPU partitioning. "list2mask --nohz" will +take the CPU list provided as nohz_full kernel boot parameter and translate +this into a CPU mask suitable for the partrt create command. + +To make life interesting, you can play with "-n" and "-i" options to runltp, to +generate some load in the non-realtime partition: + + partrt create $(list2mask --nohz) + ./runltp -n -i 5 -f partrt_nohz_full + partrt undo + +To get some more help with the cause of a failing test, do this as root before +running the test: + + echo 1 > /sys/kernel/debug/tracing/events/enable + +and then look at trace after test failed: + + cat /sys/kernel/debug/tracing/trace + +Since "count_ticks" also uses ftrace and will update tracing_cpumask to only +trace the nohz_full CPUs, the trace should be close to empty if the run was +successful. diff --git a/testcases/kernel/partrt/nohz_full/.gitignore b/testcases/kernel/partrt/nohz_full/.gitignore new file mode 100644 index 0000000..5848b21 --- /dev/null +++ b/testcases/kernel/partrt/nohz_full/.gitignore @@ -0,0 +1 @@ +/test_partrt_nohz_full diff --git a/testcases/kernel/partrt/nohz_full/Makefile b/testcases/kernel/partrt/nohz_full/Makefile new file mode 100644 index 0000000..90e9d3f --- /dev/null +++ b/testcases/kernel/partrt/nohz_full/Makefile @@ -0,0 +1,29 @@ +# +# Copyright (C) 2014, Enea AB. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +# the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk + +CFLAGS += -D_GNU_SOURCE -Werror + +LDLIBS += -lrt + +MAKE_TARGETS := test_partrt_nohz_full + +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c b/testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c new file mode 100644 index 0000000..750ab45 --- /dev/null +++ b/testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c @@ -0,0 +1,636 @@ +/* + * Copyright (C) 2014, Enea AB. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <errno.h> +#include <string.h> +#include <getopt.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/syscall.h> +#include <sys/stat.h> +#include <stdarg.h> +#include <inttypes.h> +#include <limits.h> +#include <time.h> +#include <sched.h> +#include <fcntl.h> + +#include <test.h> +#include <safe_macros.h> + +/* Name of test case */ +const char *TCID = "nohz_full"; + +/* Number of expected "TPASS" */ +const int TST_TOTAL = 1; + +/* Used for RT load */ +volatile int dummy_value; + +/* When true (1), all children will terminate */ +static volatile int time_is_up = 0; + +/* Verbosity level chosen from program command line. + * 0 = Only errors and status reported + * 1 = Show info() messages + * 2 = Request verbose information from called sub-programs + * 3 = Request more verbose inormation from called sub-programs. + */ +static int verbose = 0; + +/* Name of application as given in argv[0] */ +static const char *appname; + +/* RT load function prototype */ +typedef void (child_func)(void); + +/* Report error a'la printf() and terminate test */ +#define err(MSG...) tst_brkm(TBROK, cleanup, MSG) + +/* Report test failed. Will not terminate test execution. */ +#define fail(MSG...) tst_resm(TFAIL, MSG) + +/* Report test passed. Will not termiunate test execution. */ +#define pass(MSG...) tst_resm(TPASS, MSG) + +/* Print information if verbose has been selected */ +#define info(MSG...) do { if (verbose > 0) tst_resm(TINFO, MSG); } while (0) + +/* Amount of stack for RT load threads */ +#define STACK_SZ (64 * 1024) + +static void cleanup(void); + +/* + * Check exit status value. + * If any signs of error is found, report error and abort testing. + */ +static void assert_exit_status(const char *cmd, int status) +{ + if (WIFSIGNALED(status)) + err("%s: Child terminated unexpectedly due to signal nr %d", + cmd, + WTERMSIG(status)); + if (!WIFEXITED(status)) + err("%s: Child terminated unexpectedly", + cmd); + if (WEXITSTATUS(status) != 0) + err("%s: Child exited with exit status %d", + cmd, + WEXITSTATUS(status)); +} + +/* + * Generate a load that is guaranteed not to provoke ticks. + */ +static void load_rt(void) +{ + while (!time_is_up) { + dummy_value = rand(); + } +} + +#define READ 0 +#define WRITE 1 + +/* + * Execute command, supplying data to the commands stdin using infp if infp + * is non-NULL, and supplying data output from the commands stdout using + * outfp if outfp is non-NULL. + * The command returns the pid of the command executed. + */ +static pid_t +pid_popen(const char *cmd, FILE **infp, FILE **outfp) +{ + int p_stdin[2], p_stdout[2]; + pid_t pid; + + if (((infp != NULL) && (pipe(p_stdin) != 0)) || + ((outfp != NULL) && (pipe(p_stdout) != 0))) + err("%s: Failed executing command: pipe(): %s", + cmd, strerror(errno)); + + pid = fork(); + + if (pid < 0) + err("%s: Failed executing command: fork(): %s", + cmd, strerror(errno)); + + if (pid == 0) { + if ((infp != NULL) && (dup2(p_stdin[READ], READ) == -1)) + err("%s: pid %lu: dup() to stdin failed: %s", + cmd, (unsigned long) pid, strerror(errno)); + if ((outfp != NULL) && (dup2(p_stdout[WRITE], WRITE) == -1)) + err("%s: pid %lu: dup() to stdout failed: %s", + cmd, (unsigned long) pid, strerror(errno)); + + /* Close all unneeded descriptors */ + if ( + ((infp != NULL) && + ((close(p_stdin[READ]) == -1) || + (close(p_stdin[WRITE]) == -1))) || + ((outfp != NULL) && + ((close(p_stdout[READ]) == -1) || + (close(p_stdout[WRITE]) == -1)))) + err("%s: pid %lu: Child: close() of pipe failed: %s", + cmd, (unsigned long) pid, strerror(errno)); + + execl("/bin/sh", "sh", "-c", cmd, NULL); + + err("%s: pid %lu: exec() error: %s", + cmd, (unsigned long) pid, strerror(errno)); + } + + if (((infp != NULL) && (close(p_stdin[READ]) == -1)) || + ((outfp != NULL) && (close(p_stdout[WRITE]) == -1))) + err("%s: pid %lu: Parent: close() of pipe failed: %s", + cmd, (unsigned long) pid, strerror(errno)); + + if (infp != NULL) { + (*infp) = fdopen(p_stdin[WRITE], "w"); + if (*infp == NULL) + err("%s: pid %lu: fdopen() of stdin failed: %s", + cmd, (unsigned long) pid, strerror(errno)); + } + + if (outfp != NULL) { + (*outfp) = fdopen(p_stdout[READ], "r"); + if (*outfp == NULL) + err("%s: pid %lu: fdopen() of stdout failed: %s", + cmd, (unsigned long) pid, strerror(errno)); + } + + return pid; +} + +/* + * Wait for a command with given pid to finish, and assert that the command + * returned success (0). + */ +static void +pid_wait(pid_t pid) +{ + int status; + char prefix[64]; + + snprintf(prefix, sizeof (prefix), "Pid %lu", (unsigned long) pid); + + if (wait(&status) == -1) + err("%s: wait() failed: %s", prefix, strerror(errno)); + + assert_exit_status(prefix, status); +} + +/* + * Execute given command using shell, ensure success (0) is returned. + */ +static void shell(const char *cmd, ...) +{ + char *cmd_buf; + va_list va; + + va_start(va, cmd); + if (vasprintf(&cmd_buf, cmd, va) == -1) + err("%s: Not valid printf format, or out of memory", cmd); + va_end(va); + + info("%s: Executing", cmd_buf); + pid_wait(pid_popen(cmd_buf, NULL, NULL)); + info("%s: Returns success", cmd_buf); + + free(cmd_buf); +} + +/* + * Execute given command using shell, ensure success (0) is returned. + * Only reports errors. + */ +static void shell_silent(const char *cmd, ...) +{ + char *cmd_buf; + va_list va; + + va_start(va, cmd); + if (vasprintf(&cmd_buf, cmd, va) == -1) + err("%s: Not valid printf format, or out of memory", cmd); + va_end(va); + + pid_wait(pid_popen(cmd_buf, NULL, NULL)); + + free(cmd_buf); +} + +/* + * Convert string to unsigned long. + * Error prefix expressed as string. + */ +static unsigned long str_to_ulong( + const char *str, + const char *err_prefix, + int base + ) +{ + char *endptr; + unsigned long val; + val = strtoull(str, &endptr, base); + if (endptr == str) + err("%s: %s: Expected unsigned decimal value", err_prefix, str); + if (*endptr != '\0') + err("%s: %s: Garbage character '%c' found after decimal value", + err_prefix, str, *endptr); + + return val; +} + +/* + * Call a command using shell. + * Command line expressed as va_list. + * Returns the command's first line of output. + */ +static char * shell_str(char *dest, int size, const char *cmd, ...) +{ + int len; + char *cmd_buf; + FILE *file; + pid_t pid; + va_list va; + + /* Build command line */ + va_start(va, cmd); + if (vasprintf(&cmd_buf, cmd, va) == -1) + err("%s: Not valid printf format, or out of memory", cmd); + va_end(va); + + /* Launch command */ + pid = pid_popen(cmd_buf, NULL, &file); + info("%s: Executing, pid %lu", cmd_buf, (unsigned long) pid); + + /* Read commands stdout */ + if (fgets(dest, size, file) == NULL) { + if (feof(file)) + err("%s: pid %lu: Expected output from the command, but got nothing", + cmd_buf, (unsigned long) pid); + else + err("%s: pid %lu: Could not read command output: %s", + cmd_buf, (unsigned long) pid, strerror(errno)); + } + + /* Get rid of terminating newline, if any */ + len = strlen(dest); + if (dest[len-1] == '\n') + dest[len-1] = '\0'; + + /* Wait until command execution finish. + * Main reason for doing this even though we've got what we want is for + * better error detection. The alternative could be to let cleanup() + * handle it if process hasn't finished by then. */ + pid_wait(pid); + + info("%s: Returns: %s", cmd_buf, dest); + + free(cmd_buf); + + return dest; +} + +/* + * Call a command using shell. + * Command line expressed similar to printf(). + * Expects that the command outputs a value as the first line of output + * from stdout. + * Asserts that the command returned success (0). + */ +static unsigned long shell_int(int base, const char *cmd, ...) +{ + va_list va; + unsigned long val; + char val_str[64]; + char *cmd_buf; + + va_start(va, cmd); + if (vasprintf(&cmd_buf, cmd, va) == -1) + err("%s: Not valid printf format, or out of memory", cmd); + va_end(va); + val = str_to_ulong( + shell_str(val_str, sizeof (val_str), "%s", cmd), cmd, base); + + return val; +} + +/* + * Struct used for synchronization between child_entry() and launch_child(). + */ +struct child_params { + /* Function to run */ + child_func *func; + + /* cpuset partition to run in */ + const char *cpu_partition; + + /* CPU ID to run on. Must be one of the CPUs in chosen cpu_partition */ + int cpu; + + /* Written by child_entry() when no more activities that might provoke + * ticks are expected. */ + volatile int started; +}; + +/* + * Child tramponline function for clone() function. + */ +static int child_entry(void *arg) +{ + const int child_tid = syscall(SYS_gettid); + struct child_params *child_params = arg; + + /* Try to be quiet, or else we might provoke ticks */ + shell_silent("partrt move %d %s > /dev/null", + child_tid, child_params->cpu_partition); + if (child_params->cpu >= 0) + shell_silent("taskset -p %#lx %d > /dev/null", + (unsigned long) 1 << child_params->cpu, + child_tid); + + /* Signal parent that this child now enters RT load function + * (i.e. no more ticks are expected) */ + child_params->started = 1; + + child_params->func(); + return 0; +} + +/* + * Start a new thread executing the given function, on the given cpuset + * partition and CPU ID. Note that the CPU ID must be one of the + * available CPUs in the cpuset partition. + */ +static void launch_child( + child_func *func, + const char *cpu_partition, + int cpu) +{ + char *stack = SAFE_MALLOC(cleanup, STACK_SZ); + int tid; + struct child_params child_params = { + func, cpu_partition, cpu, 0 + }; + + tid = ltp_clone(CLONE_VM, child_entry, &child_params, STACK_SZ, stack); + if (tid == -1) + err ("clone(): %s", strerror(errno)); + + info("tid %d: Starting %s load on cpu %d", tid, cpu_partition, cpu); + while (!child_params.started) + sched_yield(); +} + +/* + * Perform test cleanup. + * Run both when testing is finished, as well as when test is terminated + * abnormally. + */ +static void cleanup(void) +{ + /* Prohibits multiple execution of cleanup function */ + static volatile int cleanup_entered = 0; + + pid_t pid; + int status; + + if (!__sync_bool_compare_and_swap(&cleanup_entered, 0, 1, + cleanup_entered)) { + info("Cleanup: Already cleaning, exiting"); + return; + } + + info("Cleanup: Terminating children"); + + time_is_up = 1; + + do { + pid = wait(&status); + if (pid != -1) { + char cmd[64]; + snprintf(cmd, sizeof (cmd), "Pid %lu", + (unsigned long) pid); + assert_exit_status(cmd, status); + info("Cleanup: %s: Has terminated successfully", cmd); + } + } while (pid != -1); + + if (errno != ECHILD) + err("Cleanup: wait() failed: %s", strerror(errno)); + + info("Cleanup: Done"); +} + +static unsigned long determine_nohz_mask(void) +{ + const char *nohz_cpus_filename = "/sys/fs/cgroup/cpuset/rt/cpuset.cpus"; + int file = open(nohz_cpus_filename, O_RDONLY); + char buf[256] = {0}; + char *curr; + int range_first; + int range_last; + unsigned long mask = 0; + + if (file == -1) + err("%s: Open failed, have you run 'partrt create'?", + nohz_cpus_filename); + + SAFE_READ(cleanup, 0, file, buf, sizeof (buf) - 1); + + curr = buf; + while (*curr != '\0') { + char *curr_next; + int bit; + + range_first = range_last = strtoull(curr, &curr_next, 10); + if (curr == curr_next) + err("%s: %s: Cannot parse CPU list at %s", + nohz_cpus_filename, buf, curr); + curr = curr_next; + if (*curr == '-') { + curr++; + range_last = strtoull(curr, &curr_next, 10); + if (curr == curr_next) + err("%s: %s: Cannot parse CPU list at %s", + nohz_cpus_filename, buf, curr); + curr = curr_next; + } + + /* Set all bits in range */ + for (bit = range_first; bit <= range_last; bit++) { + mask |= (1 << bit); + } + + if ((*curr == ',') || (*curr == '\n') || (*curr == '\r')) + curr++; + } + + return mask; +} + +/* + * Prepare for test. + * Perform CPU partitioning and start RT load. + */ +static int setup(void) +{ + const unsigned long nohz_mask = determine_nohz_mask(); + int bit; + int nr_children = 0; + + info("Nohz CPU mask: %#lx", nohz_mask); + tst_require_root(NULL); + for (bit = 0; bit < (int) (sizeof (nohz_mask) * 8); bit++) { + if (nohz_mask & (1lu << bit)) { + launch_child(load_rt, "rt", bit); + nr_children++; + } + } + + info("All children started"); + return nr_children; +} + +/* + * Test main function. + * Perform tick measurement for the given number of seconds. + */ +static void test(time_t duration) +{ + uint64_t nr_ticks; + time_t time_finished; + /* If sched_tick_max_deferment patch has been applied, expect that the + * partitioning has disabled ticks completely. Otherwise, expect + * 1Hz ticks */ + const int expect_0hz = + access("/sys/kernel/debug/sched_tick_max_deferment", F_OK) == 0; + const time_t current_time = time(NULL); + + const unsigned int nr_children = setup(); + const uint64_t expected_ticks = expect_0hz ? 0 : duration * nr_children; + + shell("count_ticks --cpu rt --start"); + + time_finished = time(NULL) + duration; + + info("Execution started: %s", ctime(¤t_time)); + info("Execution ends : %s", ctime(&time_finished)); + + while (time(NULL) != time_finished); + + nr_ticks = shell_int(0, "count_ticks --cpu rt --batch --end"); + + if (expect_0hz) { + if (nr_ticks != 0) + fail("Expected no ticks, but got %" PRIu64, nr_ticks); + else + pass("No ticks occurred"); + } else { + if (nr_ticks > expected_ticks) + fail("Expected maximum %" PRIu64 " ticks, but got %" PRIu64, + expected_ticks, nr_ticks); + else + pass("%" PRIu64 " ticks occurred, which was expected", + nr_ticks); + } + + cleanup(); + tst_exit(); +} + +/* + * Show usage text and exit. + */ +static void usage(void) +{ + printf( + "Usage: %s [options]\n" + " %s --help\n" + "Test whether a CPU can be isolated and made tickless even under load.\n" + "\n" + " -h, --help Display this usage text and exit.\n" + " -d, --duration Number of seconds to run the test.\n" + , + appname, appname + ); + + exit(1); +} + +/* + * Program entry + */ +int main(int argc, char *argv[]) +{ + time_t duration = 0; + int done = 0; + + static const struct option options[] = { + {"verbose", optional_argument, NULL, 'v'}, + {"duration", required_argument, NULL, 'd'}, + {"help", no_argument, NULL, 'h'} + }; + + static const char short_options[] = "v::d:p:h"; + + appname = basename(argv[0]); + + while (!done) { + const int opt = getopt_long( + argc, argv, short_options, options, NULL); + + switch (opt) { + case -1: + done = 1; + break; + case '?': + err("Use '--help' for usage"); + case 'h': + usage(); + case 'd': + duration = + (time_t) str_to_ulong(optarg, "--duration", 0); + break; + case 'v': + if (optarg != NULL) + verbose = str_to_ulong(optarg, "--verbose", 0); + else + verbose = 1; + break; + default: + err("-%c: Missing implementation for argument", opt); + } + + } + + if (duration == 0) + err("No duration specified, nothing to do"); + + info("%s: Compiled %s %s", __FILE__, __DATE__, __TIME__); + info("This test uses partrt, list2mask and count_ticks tools"); + info("These tools can be found at: <https://github.com/OpenEneaLinux/rt-tools>"); + info("Make sure you have run 'partrt create <cpumask>' before this test"); + + test(duration); + + /* Should not end up here */ + return 1; +} -- 1.7.10.4 ------------------------------------------------------------------------------ Learn Graph Databases - Download FREE O'Reilly Book "Graph Databases" is the definitive new guide to graph databases and their applications. Written by three acclaimed leaders in the field, this first edition is now available. Download your free book today! http://p.sf.net/sfu/NeoTech _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [LTP] [RFC][PATCH] partrt_nohz_full: Introducing a new test case 2014-04-16 15:48 ` Mats Liljegren @ 2014-04-22 15:47 ` chrubis [not found] ` <20140423124410.29874232@mats-desktop> [not found] ` <20140424105218.5cd2b5bf@mats-desktop> 0 siblings, 2 replies; 28+ messages in thread From: chrubis @ 2014-04-22 15:47 UTC (permalink / raw) To: Mats Liljegren; +Cc: ltp-list, Kevin Hilman, Frederic Weisbecker Hi! > +top_srcdir ?= ../../../.. > + > +include $(top_srcdir)/include/mk/testcases.mk > + > +CFLAGS += -D_GNU_SOURCE -Werror We usually define _GNU_SOURCE on the top of the C source and please remove the -Werror when you are done with testing (these flags should be controlled by the top level configure only). > +LDLIBS += -lrt > + > +MAKE_TARGETS := test_partrt_nohz_full > + > +include $(top_srcdir)/include/mk/generic_leaf_target.mk > diff --git a/testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c b/testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c > new file mode 100644 > index 0000000..750ab45 > --- /dev/null > +++ b/testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c > @@ -0,0 +1,636 @@ > +/* > + * Copyright (C) 2014, Enea AB. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See > + * the GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > + */ > + > +#include <errno.h> > +#include <string.h> > +#include <getopt.h> > +#include <sys/types.h> > +#include <sys/wait.h> > +#include <sys/syscall.h> > +#include <sys/stat.h> > +#include <stdarg.h> > +#include <inttypes.h> > +#include <limits.h> > +#include <time.h> > +#include <sched.h> > +#include <fcntl.h> > + > +#include <test.h> > +#include <safe_macros.h> > + > +/* Name of test case */ > +const char *TCID = "nohz_full"; This should be 1:1 with the test binary name. I would wote for naming both 'partrt_nohz_full' or similar. > +/* Number of expected "TPASS" */ > +const int TST_TOTAL = 1; No useless comments like this please (see paragraph 1.4 in Test Writing Guidelines). > +/* Used for RT load */ > +volatile int dummy_value; > + > +/* When true (1), all children will terminate */ > +static volatile int time_is_up = 0; > + > +/* Verbosity level chosen from program command line. > + * 0 = Only errors and status reported > + * 1 = Show info() messages > + * 2 = Request verbose information from called sub-programs > + * 3 = Request more verbose inormation from called sub-programs. > + */ > +static int verbose = 0; > + > +/* Name of application as given in argv[0] */ > +static const char *appname; > + > +/* RT load function prototype */ > +typedef void (child_func)(void); > + > +/* Report error a'la printf() and terminate test */ > +#define err(MSG...) tst_brkm(TBROK, cleanup, MSG) > + > +/* Report test failed. Will not terminate test execution. */ > +#define fail(MSG...) tst_resm(TFAIL, MSG) > + > +/* Report test passed. Will not termiunate test execution. */ > +#define pass(MSG...) tst_resm(TPASS, MSG) > + > +/* Print information if verbose has been selected */ > +#define info(MSG...) do { if (verbose > 0) tst_resm(TINFO, MSG); } while (0) Defining such macros is no no (see paragraph 1.1 in Test Writing Guidelines). > +/* Amount of stack for RT load threads */ > +#define STACK_SZ (64 * 1024) > + > +static void cleanup(void); > + > +/* > + * Check exit status value. > + * If any signs of error is found, report error and abort testing. > + */ > +static void assert_exit_status(const char *cmd, int status) > +{ > + if (WIFSIGNALED(status)) > + err("%s: Child terminated unexpectedly due to signal nr %d", > + cmd, > + WTERMSIG(status)); > + if (!WIFEXITED(status)) > + err("%s: Child terminated unexpectedly", > + cmd); > + if (WEXITSTATUS(status) != 0) > + err("%s: Child exited with exit status %d", > + cmd, > + WEXITSTATUS(status)); > +} > + > +/* > + * Generate a load that is guaranteed not to provoke ticks. > + */ > +static void load_rt(void) > +{ > + while (!time_is_up) { > + dummy_value = rand(); > + } > +} > + > +#define READ 0 > +#define WRITE 1 Use either 0 and 1 or STDIN_FILENO and STDOUT_FILENO > +/* > + * Execute command, supplying data to the commands stdin using infp if infp > + * is non-NULL, and supplying data output from the commands stdout using > + * outfp if outfp is non-NULL. > + * The command returns the pid of the command executed. > + */ > +static pid_t > +pid_popen(const char *cmd, FILE **infp, FILE **outfp) > +{ > + int p_stdin[2], p_stdout[2]; > + pid_t pid; > + > + if (((infp != NULL) && (pipe(p_stdin) != 0)) || > + ((outfp != NULL) && (pipe(p_stdout) != 0))) > + err("%s: Failed executing command: pipe(): %s", > + cmd, strerror(errno)); > + > + pid = fork(); > + > + if (pid < 0) > + err("%s: Failed executing command: fork(): %s", > + cmd, strerror(errno)); > + > + if (pid == 0) { > + if ((infp != NULL) && (dup2(p_stdin[READ], READ) == -1)) > + err("%s: pid %lu: dup() to stdin failed: %s", > + cmd, (unsigned long) pid, strerror(errno)); > + if ((outfp != NULL) && (dup2(p_stdout[WRITE], WRITE) == -1)) > + err("%s: pid %lu: dup() to stdout failed: %s", > + cmd, (unsigned long) pid, strerror(errno)); > + > + /* Close all unneeded descriptors */ > + if ( > + ((infp != NULL) && > + ((close(p_stdin[READ]) == -1) || > + (close(p_stdin[WRITE]) == -1))) || > + ((outfp != NULL) && > + ((close(p_stdout[READ]) == -1) || > + (close(p_stdout[WRITE]) == -1)))) > + err("%s: pid %lu: Child: close() of pipe failed: %s", > + cmd, (unsigned long) pid, strerror(errno)); This is too ugly. > + execl("/bin/sh", "sh", "-c", cmd, NULL); > + > + err("%s: pid %lu: exec() error: %s", > + cmd, (unsigned long) pid, strerror(errno)); > + } > + > + if (((infp != NULL) && (close(p_stdin[READ]) == -1)) || > + ((outfp != NULL) && (close(p_stdout[WRITE]) == -1))) > + err("%s: pid %lu: Parent: close() of pipe failed: %s", > + cmd, (unsigned long) pid, strerror(errno)); > + > + if (infp != NULL) { > + (*infp) = fdopen(p_stdin[WRITE], "w"); > + if (*infp == NULL) > + err("%s: pid %lu: fdopen() of stdin failed: %s", > + cmd, (unsigned long) pid, strerror(errno)); > + } > + > + if (outfp != NULL) { > + (*outfp) = fdopen(p_stdout[READ], "r"); > + if (*outfp == NULL) > + err("%s: pid %lu: fdopen() of stdout failed: %s", > + cmd, (unsigned long) pid, strerror(errno)); > + } > + > + return pid; > +} Did you just reinvented popen() with additional parameters that does not get used? Because the infp is always NULL. Okay this returns pid that can be used for wait() but even then the code is too complex for what it does. Please do not implement functionality that is not used. This only overcomplicates testcases and creates code that will break sooner or later. > +/* > + * Wait for a command with given pid to finish, and assert that the command > + * returned success (0). > + */ > +static void > +pid_wait(pid_t pid) > +{ > + int status; > + char prefix[64]; > + > + snprintf(prefix, sizeof (prefix), "Pid %lu", (unsigned long) pid); > + > + if (wait(&status) == -1) > + err("%s: wait() failed: %s", prefix, strerror(errno)); Use SAFE_WAIT() instead. > + assert_exit_status(prefix, status); You should pass the pid directly instead of the prefix, see below. > +} > + > +/* > + * Execute given command using shell, ensure success (0) is returned. > + */ > +static void shell(const char *cmd, ...) > +{ > + char *cmd_buf; > + va_list va; > + > + va_start(va, cmd); > + if (vasprintf(&cmd_buf, cmd, va) == -1) > + err("%s: Not valid printf format, or out of memory", cmd); > + va_end(va); > + > + info("%s: Executing", cmd_buf); > + pid_wait(pid_popen(cmd_buf, NULL, NULL)); > + info("%s: Returns success", cmd_buf); > + > + free(cmd_buf); > +} > + > +/* > + * Execute given command using shell, ensure success (0) is returned. > + * Only reports errors. > + */ > +static void shell_silent(const char *cmd, ...) > +{ > + char *cmd_buf; > + va_list va; > + > + va_start(va, cmd); > + if (vasprintf(&cmd_buf, cmd, va) == -1) > + err("%s: Not valid printf format, or out of memory", cmd); > + va_end(va); > + > + pid_wait(pid_popen(cmd_buf, NULL, NULL)); > + > + free(cmd_buf); > +} Moreove these two functions are almost duplicate for no good reason. More generally several shell_* wrappers with sligtly different parameters polute the code. > +/* > + * Convert string to unsigned long. > + * Error prefix expressed as string. > + */ > +static unsigned long str_to_ulong( > + const char *str, > + const char *err_prefix, > + int base > + ) > +{ > + char *endptr; > + unsigned long val; > + val = strtoull(str, &endptr, base); > + if (endptr == str) > + err("%s: %s: Expected unsigned decimal value", err_prefix, str); > + if (*endptr != '\0') > + err("%s: %s: Garbage character '%c' found after decimal value", > + err_prefix, str, *endptr); > + > + return val; > +} Use SAFE_STRTOUL() instead. > +/* > + * Call a command using shell. > + * Command line expressed as va_list. > + * Returns the command's first line of output. > + */ > +static char * shell_str(char *dest, int size, const char *cmd, ...) > +{ > + int len; > + char *cmd_buf; > + FILE *file; > + pid_t pid; > + va_list va; > + > + /* Build command line */ > + va_start(va, cmd); > + if (vasprintf(&cmd_buf, cmd, va) == -1) > + err("%s: Not valid printf format, or out of memory", cmd); > + va_end(va); > + > + /* Launch command */ > + pid = pid_popen(cmd_buf, NULL, &file); > + info("%s: Executing, pid %lu", cmd_buf, (unsigned long) pid); > + > + /* Read commands stdout */ > + if (fgets(dest, size, file) == NULL) { > + if (feof(file)) > + err("%s: pid %lu: Expected output from the command, but got nothing", > + cmd_buf, (unsigned long) pid); > + else > + err("%s: pid %lu: Could not read command output: %s", > + cmd_buf, (unsigned long) pid, strerror(errno)); > + } > + > + /* Get rid of terminating newline, if any */ > + len = strlen(dest); > + if (dest[len-1] == '\n') > + dest[len-1] = '\0'; > + > + /* Wait until command execution finish. > + * Main reason for doing this even though we've got what we want is for > + * better error detection. The alternative could be to let cleanup() > + * handle it if process hasn't finished by then. */ > + pid_wait(pid); > + > + info("%s: Returns: %s", cmd_buf, dest); > + > + free(cmd_buf); > + > + return dest; > +} Reinventing the wheel? This could be implemented with just va_args, popen(), fgets() and pclose(). No need to roll custom functions for the function at all. > +/* > + * Call a command using shell. > + * Command line expressed similar to printf(). > + * Expects that the command outputs a value as the first line of output > + * from stdout. > + * Asserts that the command returned success (0). > + */ > +static unsigned long shell_int(int base, const char *cmd, ...) > +{ > + va_list va; > + unsigned long val; > + char val_str[64]; > + char *cmd_buf; > + > + va_start(va, cmd); > + if (vasprintf(&cmd_buf, cmd, va) == -1) > + err("%s: Not valid printf format, or out of memory", cmd); > + va_end(va); > + val = str_to_ulong( > + shell_str(val_str, sizeof (val_str), "%s", cmd), cmd, base); > + > + return val; > +} The cmd_buf is not used nor freed, the additional parameters are ignored etc. If you are going to write shell_int() by using shell_str you must create vashell_str() first and build both shell_str and shell_int on the top of it. But given that this function is used only once I would strongy sugest to use shell_str() and SAFE_STRTOUL() in the caller. > +/* > + * Struct used for synchronization between child_entry() and launch_child(). > + */ > +struct child_params { > + /* Function to run */ > + child_func *func; > + > + /* cpuset partition to run in */ > + const char *cpu_partition; > + > + /* CPU ID to run on. Must be one of the CPUs in chosen cpu_partition */ > + int cpu; > + > + /* Written by child_entry() when no more activities that might provoke > + * ticks are expected. */ > + volatile int started; > +}; > + > +/* > + * Child tramponline function for clone() function. > + */ > +static int child_entry(void *arg) > +{ > + const int child_tid = syscall(SYS_gettid); > + struct child_params *child_params = arg; > + > + /* Try to be quiet, or else we might provoke ticks */ > + shell_silent("partrt move %d %s > /dev/null", > + child_tid, child_params->cpu_partition); > + if (child_params->cpu >= 0) > + shell_silent("taskset -p %#lx %d > /dev/null", > + (unsigned long) 1 << child_params->cpu, > + child_tid); You must not call tst_* interface from the child process and by extension you cannot call shell_silent() from here. See paragraph 2.2.7 in the Test Writing Guidelines. > + /* Signal parent that this child now enters RT load function > + * (i.e. no more ticks are expected) */ > + child_params->started = 1; > + > + child_params->func(); > + return 0; > +} > + > +/* > + * Start a new thread executing the given function, on the given cpuset > + * partition and CPU ID. Note that the CPU ID must be one of the > + * available CPUs in the cpuset partition. > + */ > +static void launch_child( > + child_func *func, > + const char *cpu_partition, > + int cpu) This should fit to one line with less than 80 chars. > +{ > + char *stack = SAFE_MALLOC(cleanup, STACK_SZ); > + int tid; > + struct child_params child_params = { > + func, cpu_partition, cpu, 0 > + }; > + > + tid = ltp_clone(CLONE_VM, child_entry, &child_params, STACK_SZ, stack); Is there any reason to use clone instead of fork() of pthread_create()? > + if (tid == -1) > + err ("clone(): %s", strerror(errno)); ^ no spaces between function names and brackets Have you used checkpatch.pl as suggested in the Test Writing Guidelines? > + info("tid %d: Starting %s load on cpu %d", tid, cpu_partition, cpu); > + while (!child_params.started) > + sched_yield(); I do not like the bussy loop here much (We have a pipe base child parent synchronization code in the LTP library, see paragraph 2.2.7 in the Test Writing Guidelines) but given that all the childs do is bussy loop... > +} > + > +/* > + * Perform test cleanup. > + * Run both when testing is finished, as well as when test is terminated > + * abnormally. > + */ > +static void cleanup(void) > +{ > + /* Prohibits multiple execution of cleanup function */ > + static volatile int cleanup_entered = 0; This is only one reason why you cannot call tst_ interface from the child processes. Remove this and fix the rest of code. > + pid_t pid; > + int status; > + > + if (!__sync_bool_compare_and_swap(&cleanup_entered, 0, 1, > + cleanup_entered)) { > + info("Cleanup: Already cleaning, exiting"); > + return; > + } > + > + info("Cleanup: Terminating children"); > + > + time_is_up = 1; > + > + do { > + pid = wait(&status); > + if (pid != -1) { > + char cmd[64]; > + snprintf(cmd, sizeof (cmd), "Pid %lu", > + (unsigned long) pid); > + assert_exit_status(cmd, status); I do not understand why you pass "Pid %lu" string instead of the pid. It's more messy this way. > + info("Cleanup: %s: Has terminated successfully", cmd); > + } > + } while (pid != -1); > + > + if (errno != ECHILD) > + err("Cleanup: wait() failed: %s", strerror(errno)); > + > + info("Cleanup: Done"); > +} > + > +static unsigned long determine_nohz_mask(void) > +{ > + const char *nohz_cpus_filename = "/sys/fs/cgroup/cpuset/rt/cpuset.cpus"; > + int file = open(nohz_cpus_filename, O_RDONLY); this is not file but fd (file descriptor) > + char buf[256] = {0}; > + char *curr; > + int range_first; > + int range_last; > + unsigned long mask = 0; > + > + if (file == -1) > + err("%s: Open failed, have you run 'partrt create'?", > + nohz_cpus_filename); > + > + SAFE_READ(cleanup, 0, file, buf, sizeof (buf) - 1); > + > + curr = buf; > + while (*curr != '\0') { > + char *curr_next; > + int bit; > + > + range_first = range_last = strtoull(curr, &curr_next, 10); > + if (curr == curr_next) > + err("%s: %s: Cannot parse CPU list at %s", > + nohz_cpus_filename, buf, curr); > + curr = curr_next; > + if (*curr == '-') { > + curr++; > + range_last = strtoull(curr, &curr_next, 10); > + if (curr == curr_next) > + err("%s: %s: Cannot parse CPU list at %s", > + nohz_cpus_filename, buf, curr); > + curr = curr_next; > + } > + > + /* Set all bits in range */ > + for (bit = range_first; bit <= range_last; bit++) { > + mask |= (1 << bit); > + } > + > + if ((*curr == ',') || (*curr == '\n') || (*curr == '\r')) > + curr++; You may simplify this part with fmemopen() and sscanf() (which deals with whitespaces and conversions). Once you have FILE * handle it should be as simple as: (beweare untested) while (sscanf("%d-%d[^0-9]*", &range_first, &range_last) == 2) { //modify bitmask } > + } > + > + return mask; > +} > + > +/* > + * Prepare for test. > + * Perform CPU partitioning and start RT load. > + */ > +static int setup(void) > +{ > + const unsigned long nohz_mask = determine_nohz_mask(); > + int bit; > + int nr_children = 0; > + > + info("Nohz CPU mask: %#lx", nohz_mask); > + tst_require_root(NULL); > + for (bit = 0; bit < (int) (sizeof (nohz_mask) * 8); bit++) { > + if (nohz_mask & (1lu << bit)) { > + launch_child(load_rt, "rt", bit); > + nr_children++; > + } > + } > + > + info("All children started"); > + return nr_children; > +} The setup should exit with tst_brkm(TCONF, "Cpuset not available") in case cpuset is not compiled in kernel. The easiest way would be trying access() on suitable sysfs path such as "/sys/fs/cgroup/cpuset/". > +/* > + * Test main function. > + * Perform tick measurement for the given number of seconds. > + */ > +static void test(time_t duration) > +{ > + uint64_t nr_ticks; > + time_t time_finished; > + /* If sched_tick_max_deferment patch has been applied, expect that the > + * partitioning has disabled ticks completely. Otherwise, expect > + * 1Hz ticks */ > + const int expect_0hz = > + access("/sys/kernel/debug/sched_tick_max_deferment", F_OK) == 0; This is a bit hacky, I would just use if (...) instead. > + const time_t current_time = time(NULL); > + > + const unsigned int nr_children = setup(); Returning number of children from setup is a bit confusing. Maybe it would be better to make nr_children global variable or rename the function. > + const uint64_t expected_ticks = expect_0hz ? 0 : duration * nr_children; > + > + shell("count_ticks --cpu rt --start"); > + > + time_finished = time(NULL) + duration; > + > + info("Execution started: %s", ctime(¤t_time)); > + info("Execution ends : %s", ctime(&time_finished)); > + > + while (time(NULL) != time_finished); This is not safe, it should be time(NULL) > time_finished, because otherwise the test will spin forever if the one exact second was missed, either due to machine load or because ntp daenom that has updated system time while this loop was running. Generally it would be better to use clock_gettime() with CLOCK_MONOTONIC_RAW that is not subject to adjustements. > + nr_ticks = shell_int(0, "count_ticks --cpu rt --batch --end"); > + > + if (expect_0hz) { > + if (nr_ticks != 0) > + fail("Expected no ticks, but got %" PRIu64, nr_ticks); > + else > + pass("No ticks occurred"); > + } else { > + if (nr_ticks > expected_ticks) > + fail("Expected maximum %" PRIu64 " ticks, but got %" PRIu64, > + expected_ticks, nr_ticks); > + else > + pass("%" PRIu64 " ticks occurred, which was expected", > + nr_ticks); > + } > + > + cleanup(); > + tst_exit(); > +} > + > +/* > + * Show usage text and exit. > + */ > +static void usage(void) > +{ > + printf( > + "Usage: %s [options]\n" > + " %s --help\n" > + "Test whether a CPU can be isolated and made tickless even under load.\n" > + "\n" > + " -h, --help Display this usage text and exit.\n" > + " -d, --duration Number of seconds to run the test.\n" > + , > + appname, appname > + ); > + > + exit(1); > +} > + > +/* > + * Program entry > + */ Again commenting the obvious. > +int main(int argc, char *argv[]) > +{ > + time_t duration = 0; > + int done = 0; > + > + static const struct option options[] = { > + {"verbose", optional_argument, NULL, 'v'}, > + {"duration", required_argument, NULL, 'd'}, > + {"help", no_argument, NULL, 'h'} > + }; > + > + static const char short_options[] = "v::d:p:h"; > + > + appname = basename(argv[0]); > + > + while (!done) { > + const int opt = getopt_long( > + argc, argv, short_options, options, NULL); > + > + switch (opt) { > + case -1: > + done = 1; > + break; > + case '?': > + err("Use '--help' for usage"); > + case 'h': > + usage(); > + case 'd': > + duration = > + (time_t) str_to_ulong(optarg, "--duration", 0); > + break; > + case 'v': > + if (optarg != NULL) > + verbose = str_to_ulong(optarg, "--verbose", 0); > + else > + verbose = 1; > + break; > + default: > + err("-%c: Missing implementation for argument", opt); > + } > + > + } Use LTP parse_opts() instead. > + if (duration == 0) > + err("No duration specified, nothing to do"); > + > + info("%s: Compiled %s %s", __FILE__, __DATE__, __TIME__); > + info("This test uses partrt, list2mask and count_ticks tools"); > + info("These tools can be found at: <https://github.com/OpenEneaLinux/rt-tools>"); > + info("Make sure you have run 'partrt create <cpumask>' before this test"); > + > + test(duration); You should do the standard test looping here (as explained in the example testcase in the Test Writing Guidelines). I.e. for (lc = 0; TEST_LOOPING(lc); lc++) test(duration); > + /* Should not end up here */ > + return 1; You should have used cleanup() and tst_exit() here instead. -- Cyril Hrubis chrubis@suse.cz ------------------------------------------------------------------------------ Start Your Social Network Today - Download eXo Platform Build your Enterprise Intranet with eXo Platform Software Java Based Open Source Intranet - Social, Extensible, Cloud Ready Get Started Now And Turn Your Intranet Into A Collaboration Platform http://p.sf.net/sfu/ExoPlatform _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
[parent not found: <20140423124410.29874232@mats-desktop>]
* Re: [LTP] [RFC][PATCH] partrt_nohz_full: Introducing a new test case [not found] ` <20140423124410.29874232@mats-desktop> @ 2014-04-23 11:34 ` chrubis 0 siblings, 0 replies; 28+ messages in thread From: chrubis @ 2014-04-23 11:34 UTC (permalink / raw) To: Mats Liljegren; +Cc: ltp-list, Kevin Hilman, Frederic Weisbecker Hi! > As for -Werror, I can remove it, but not without shedding some tears... > This was my way of assuring that no one who contributed to my code will > be able to slip in warnings unnoticed. To turn things around: The > -Werror is a way of saying "Now this code has once compiled without > warnings, and from now on it is not ok to introduce new warnings". > > You might think that it is never ok to introduce warnings, but when I > compile I get some warnings. I didn't want this to happen to my code as > well. The real problem with -Werror is that the code usually fails to compile with older/newer compiler because false possitive warnings were removed or new warnings were added. It's good to use -Werror for development but it's not a good idea to use it for production. > > No useless comments like this please (see paragraph 1.4 in Test > > Writing Guidelines). > > Hmm, must have been some copy and paste. I'm usually bad at producing > comments... ;-) We have quite a lot of legacy code that is messed up. :( So copy pasting random test and starting from there is usually not a good idea. > > > +/* > > > + * Call a command using shell. > > > + * Command line expressed similar to printf(). > > > + * Expects that the command outputs a value as the first line of > > > output > > > + * from stdout. > > > + * Asserts that the command returned success (0). > > > + */ > > > +static unsigned long shell_int(int base, const char *cmd, ...) > > > +{ > > > + va_list va; > > > + unsigned long val; > > > + char val_str[64]; > > > + char *cmd_buf; > > > + > > > + va_start(va, cmd); > > > + if (vasprintf(&cmd_buf, cmd, va) == -1) > > > + err("%s: Not valid printf format, or out of > > > memory", cmd); > > > + va_end(va); > > > + val = str_to_ulong( > > > + shell_str(val_str, sizeof (val_str), "%s", cmd), > > > cmd, base); + > > > + return val; > > > +} > > > > The cmd_buf is not used nor freed, the additional parameters are > > ignored etc. > > > > If you are going to write shell_int() by using shell_str you must > > create vashell_str() first and build both shell_str and shell_int on > > the top of it. But given that this function is used only once I would > > strongy sugest to use shell_str() and SAFE_STRTOUL() in the caller. > > I agree. That would simplify things. Sometimes my ambition to make > things general get a bit out of hand... I have similar problems as well :) > > > +{ > > > + char *stack = SAFE_MALLOC(cleanup, STACK_SZ); > > > + int tid; > > > + struct child_params child_params = { > > > + func, cpu_partition, cpu, 0 > > > + }; > > > + > > > + tid = ltp_clone(CLONE_VM, child_entry, &child_params, > > > STACK_SZ, stack); > > > > Is there any reason to use clone instead of fork() of > > pthread_create()? > > Yep, fork() will produce a process, but I want a thread. But I also > wanted the child PID, which is why I didn't use pthread_create(). Ok. > > > + if (tid == -1) > > > + err ("clone(): %s", strerror(errno)); > > ^ > > no spaces between function names and brackets > > > > Have you used checkpatch.pl as suggested in the Test Writing > > Guidelines? > > No, but that was my intention before sending the "real" patch. This > current round was RFC since I wanted to know if this is something that > could be upstreamed before I do the final polishing on it. Ok. > > > + info("tid %d: Starting %s load on cpu %d", tid, > > > cpu_partition, cpu); > > > + while (!child_params.started) > > > + sched_yield(); > > > > I do not like the bussy loop here much (We have a pipe base child > > parent synchronization code in the LTP library, see paragraph 2.2.7 > > in the Test Writing Guidelines) but given that all the childs do is > > bussy loop... > > I'm not sure why this is a problem. Using shared memory seems like a > simpler design that using FIFO dependent on sharing the same current > working directory. I also wonder whether tst_checkpoint.h is dependent > on having different processes rather than different threads to > synchronize. The problem is that this spins in place, which is bussy waiting and this may lead to unexpected results when you fork several childrens that hogs cpu and then wait in bussy loop until they run. There were several testcases that did something similar and modified realtime priorities at the same time which, under some circumstances, hanged system (because there were no CPU time left for kernel threads). If you use FIFO the waiting process waits in kernel queue and does not consume CPU resources. The checkpoint implementation in LTP needs only a common directory so that the processes/threads/whatever agrees on the file to use. (I should add more documentation into Test Writing Guidelines about this) > The whole point with this is to make sure the children are up and > running (i.e. no shell commands left to be done at startup). And this > was the simplest design I could think of to ensure this. It's simplest but not best. > > > + info("Cleanup: %s: Has terminated > > > successfully", cmd); > > > + } > > > + } while (pid != -1); > > > + > > > + if (errno != ECHILD) > > > + err("Cleanup: wait() failed: %s", strerror(errno)); > > > + > > > + info("Cleanup: Done"); > > > +} > > > + > > > +static unsigned long determine_nohz_mask(void) > > > +{ > > > + const char *nohz_cpus_filename = > > > "/sys/fs/cgroup/cpuset/rt/cpuset.cpus"; > > > + int file = open(nohz_cpus_filename, O_RDONLY); > > > > this is not file but fd (file descriptor) > > Where the fd represents a file... Whatever. I can change the name. Not that it's wrong, but this is the first time I've seen variable named file that is not FILE*. I think that this would be more clear otherwise. But that is very minor. > > > +/* > > > + * Test main function. > > > + * Perform tick measurement for the given number of seconds. > > > + */ > > > +static void test(time_t duration) > > > +{ > > > + uint64_t nr_ticks; > > > + time_t time_finished; > > > + /* If sched_tick_max_deferment patch has been applied, > > > expect that the > > > + * partitioning has disabled ticks completely. Otherwise, > > > expect > > > + * 1Hz ticks */ > > > + const int expect_0hz = > > > + > > > access("/sys/kernel/debug/sched_tick_max_deferment", F_OK) == 0; > > > > This is a bit hacky, I would just use if (...) instead. > > I like const declaration, they make things less error prone. So > personally I prefer small amount of hackiness to buy me less error > prone code... But this is a matter of taste. Ok. Leave this one as it is if you want, but at least define the long path to some shorter name at the test start, something as: #define SCHED_TICK_MAX_FILE "/sys/kernel/debug/sched_tick_max_deferment" > > > + const time_t current_time = time(NULL); > > > + > > > + const unsigned int nr_children = setup(); > > > > Returning number of children from setup is a bit confusing. Maybe it > > would be better to make nr_children global variable or rename the > > function. > > The name "setup" doesn't really give any hints on what is returned, > only what is being done. I'm not confused by it, but I guess I'm > biased... > > Would you prefer that "setup" is renamed to "setup_return_nr_children"? It's a bit confusing in terms of LTP because there are thousand testcases where setup() is void and does not return anything. There is, on the other hand, custom of setting up global variables in the test setup(). So either use global variable or rename it to setup_children() or similar which would hint that it's different. -- Cyril Hrubis chrubis@suse.cz ------------------------------------------------------------------------------ Start Your Social Network Today - Download eXo Platform Build your Enterprise Intranet with eXo Platform Software Java Based Open Source Intranet - Social, Extensible, Cloud Ready Get Started Now And Turn Your Intranet Into A Collaboration Platform http://p.sf.net/sfu/ExoPlatform _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
[parent not found: <20140424105218.5cd2b5bf@mats-desktop>]
* Re: [LTP] [RFC][PATCH] partrt_nohz_full: Introducing a new test case [not found] ` <20140424105218.5cd2b5bf@mats-desktop> @ 2014-04-24 9:06 ` chrubis [not found] ` <20140424140358.63dac752@mats-desktop> 0 siblings, 1 reply; 28+ messages in thread From: chrubis @ 2014-04-24 9:06 UTC (permalink / raw) To: Mats Liljegren; +Cc: ltp-list, Kevin Hilman, Frederic Weisbecker Hi! > > > +/* > > > + * Wait for a command with given pid to finish, and assert that > > > the command > > > + * returned success (0). > > > + */ > > > +static void > > > +pid_wait(pid_t pid) > > > +{ > > > + int status; > > > + char prefix[64]; > > > + > > > + snprintf(prefix, sizeof (prefix), "Pid %lu", (unsigned > > > long) pid); + > > > + if (wait(&status) == -1) > > > + err("%s: wait() failed: %s", prefix, > > > strerror(errno)); > > > > Use SAFE_WAIT() instead. > > > > I couldn't find that function in include directory. Did I search in the > wrong place? Ah, you are right, that one is missing, sorry. I will add it. > > > + const char *str, > > > + const char *err_prefix, > > > + int base > > > + ) > > > +{ > > > + char *endptr; > > > + unsigned long val; > > > + val = strtoull(str, &endptr, base); > > > + if (endptr == str) > > > + err("%s: %s: Expected unsigned decimal value", > > > err_prefix, str); > > > + if (*endptr != '\0') > > > + err("%s: %s: Garbage character '%c' found after > > > decimal value", > > > + err_prefix, str, *endptr); > > > + > > > + return val; > > > +} > > > > Use SAFE_STRTOUL() instead. > > That would loose a lot of information useful for figuring out what the > error is. For example, when called for translating command line options > to integers, using SAFE_STRTOUL() will only say that "this string is > not an integer" without giving any hint that we are talking about a > command line option, and which option that is being parsed. Well it depends on the fact if the additional work is worth of the clarity. Looked at the code it may be in this case. But at least remove the base argument and just pass 10 to strtoull() instead. Keep things simple :) > > > + pid_t pid; > > > + int status; > > > + > > > + if (!__sync_bool_compare_and_swap(&cleanup_entered, 0, 1, > > > + cleanup_entered)) { > > > + info("Cleanup: Already cleaning, exiting"); > > > + return; > > > + } > > > + > > > + info("Cleanup: Terminating children"); > > > + > > > + time_is_up = 1; > > > + > > > + do { > > > + pid = wait(&status); > > > + if (pid != -1) { > > > + char cmd[64]; > > > + snprintf(cmd, sizeof (cmd), "Pid %lu", > > > + (unsigned long) pid); > > > + assert_exit_status(cmd, status); > > > > I do not understand why you pass "Pid %lu" > > string instead of the pid. It's more messy > > this way. > > This is because assert_exit_status() sometimes checks result of > executed commands, and in those cases this function gets a command line > rather than PID. I've greped the source code for assert_exit_status and it's called from pid_wait() and cleanup() in both cases the first argument is result of: snprintf(buf, sizeof(buf), "Pid %lu", pid) Do I miss something? -- Cyril Hrubis chrubis@suse.cz ------------------------------------------------------------------------------ Start Your Social Network Today - Download eXo Platform Build your Enterprise Intranet with eXo Platform Software Java Based Open Source Intranet - Social, Extensible, Cloud Ready Get Started Now And Turn Your Intranet Into A Collaboration Platform http://p.sf.net/sfu/ExoPlatform _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
[parent not found: <20140424140358.63dac752@mats-desktop>]
* Re: [LTP] [RFC][PATCH] partrt_nohz_full: Introducing a new test case [not found] ` <20140424140358.63dac752@mats-desktop> @ 2014-04-24 12:35 ` chrubis 0 siblings, 0 replies; 28+ messages in thread From: chrubis @ 2014-04-24 12:35 UTC (permalink / raw) To: Mats Liljegren; +Cc: ltp-list, Kevin Hilman, Frederic Weisbecker Hi! > > > I couldn't find that function in include directory. Did I search in > > > the wrong place? > > > > Ah, you are right, that one is missing, sorry. > > > > I will add it. > > Ok. I've pushed the changes. -- Cyril Hrubis chrubis@suse.cz ------------------------------------------------------------------------------ Start Your Social Network Today - Download eXo Platform Build your Enterprise Intranet with eXo Platform Software Java Based Open Source Intranet - Social, Extensible, Cloud Ready Get Started Now And Turn Your Intranet Into A Collaboration Platform http://p.sf.net/sfu/ExoPlatform _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [LTP] [RFC][PATCH] partrt_nohz_full: Introducing a new test case 2014-04-16 15:48 ` [LTP] [RFC][PATCH] partrt_nohz_full: Introducing a new test case Mats Liljegren 2014-04-16 15:48 ` Mats Liljegren @ 2014-04-22 14:07 ` chrubis [not found] ` <20140423084101.536f03f0@mats-desktop> 2014-04-28 15:06 ` [LTP] [PATCH v2] " Mats Liljegren 2 siblings, 1 reply; 28+ messages in thread From: chrubis @ 2014-04-22 14:07 UTC (permalink / raw) To: Mats Liljegren; +Cc: ltp-list, Kevin Hilman, Frederic Weisbecker Hi! > This patch introduces a new test case, partrt_nohz_full. > > This is sent as an RFC since as far as I understand it ltp is under > stabilization. But I still wanted to send what I've got currently to see if > there are any major issues with it. > > This test case requires a couple of tools which can be cloned from here: > > https://github.com/OpenEneaLinux/rt-tools This may be a problematic, LTP testcases should be as selfcontained as possible and should depend only on standard libraries/tools. > The tools are scripts so they are easily copied to somewhere PATH will find > them. > > The patch applies on ltp, which can be cloned from here: > > https://github.com/linux-test-project/ltp.git > > The patch applies to commit hash b24ce8f6bcf315078bdc48bbddf780b964ffa59a. > > To run the test case, do the following: > > partrt create <cpumask> Again selfcontained, the testcase should be able to run just by runltp. Any preparation and cleanup should be done in the testcase. > runltp -f partrt_nohz_full > > where <cpumask> should be a hexadecimal mask of nohz_full kernel boot parameter. > If you're too lazy to translate it by hand, you can use list2mask tool from > the rt-tools repository: > > partrt create 0x$(list2mask --nohz) > runltp -f partrt_nohz_full -- Cyril Hrubis chrubis@suse.cz ------------------------------------------------------------------------------ Start Your Social Network Today - Download eXo Platform Build your Enterprise Intranet with eXo Platform Software Java Based Open Source Intranet - Social, Extensible, Cloud Ready Get Started Now And Turn Your Intranet Into A Collaboration Platform http://p.sf.net/sfu/ExoPlatform _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
[parent not found: <20140423084101.536f03f0@mats-desktop>]
* Re: [LTP] [RFC][PATCH] partrt_nohz_full: Introducing a new test case [not found] ` <20140423084101.536f03f0@mats-desktop> @ 2014-04-23 10:24 ` chrubis 0 siblings, 0 replies; 28+ messages in thread From: chrubis @ 2014-04-23 10:24 UTC (permalink / raw) To: Mats Liljegren; +Cc: ltp-list, Kevin Hilman, Frederic Weisbecker Hi! > I've thought about this. The problem is that these tools is only one way > of doing it. Performing CPU isolation in Linux is still something of a > black art, so my idea was to show one way that we have got it working > for at least some boards. Since how to do this is also dependent on > architecture, board, kernel version and kernel configuration I didn't > want to tie these script too closely to the test case since the test > case by itself is quite generic. > > Having these tools as copies in LTP repository gives additional > maintenance burden that I feel is unnecessary. The tools are needed for > other purposes as well, so moving them to LTP is not an option. > > But if you have a good idea how to solve this dilemma, I'd like to hear > about it. The best solution I can think of is to add the rt-tools as a git submodule in utils directory and install the required scripts on make install if submodules were initialized and pulled. On the test side we need to check if these scripts are in $PATH and exit with TCONF if not. Does this sound reasonable? -- Cyril Hrubis chrubis@suse.cz ------------------------------------------------------------------------------ Start Your Social Network Today - Download eXo Platform Build your Enterprise Intranet with eXo Platform Software Java Based Open Source Intranet - Social, Extensible, Cloud Ready Get Started Now And Turn Your Intranet Into A Collaboration Platform http://p.sf.net/sfu/ExoPlatform _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
* [LTP] [PATCH v2] partrt_nohz_full: Introducing a new test case 2014-04-16 15:48 ` [LTP] [RFC][PATCH] partrt_nohz_full: Introducing a new test case Mats Liljegren 2014-04-16 15:48 ` Mats Liljegren 2014-04-22 14:07 ` chrubis @ 2014-04-28 15:06 ` Mats Liljegren 2014-04-28 15:06 ` [LTP] [PATCH] " Mats Liljegren 2014-05-13 14:11 ` [LTP] [PATCH v3] " Mats Liljegren 2 siblings, 2 replies; 28+ messages in thread From: Mats Liljegren @ 2014-04-28 15:06 UTC (permalink / raw) To: ltp-list; +Cc: Frederic Weisbecker, Kevin Hilman Changes from version 1: - Style fixes proposed by checkpatch.pl script - Have rt-tools as a git sub-module instead of requiring it to be cloned manually, builds can still be done without checking out rt-tools but you can't run partrt_nohz_full test successfully then - Check if cpuset supported by kernel, TCONF error if not - Check if rt-tools has been checked out before build, TCONF error if not - Simplified determined_nohz_mask() - Use parse_opts() instead of getopt_long() when parsing command line options - Moved partrt in SUBDIRS so that it is alphabetically correct in testcases/kernel/Makefile - launch_child will now handle moving child thread to the right CPUSET partition and CPU, which also eliminates the need for synchronization between parent and child - Children will no longer be able to cause any tst_* calls - Removed shell_silent(), above changes made it obsolete - Removed base parameter to str_to_ulong(), will always be 0 now - setup_children() is now called from main() rather than test() function. - Macros defining file names - Use C code for moving children to the right place instead of external scripts. Using script invocation from RT partition using NFS mounted file system will most likely invoke ticks which can make the test case fail - Added standard test loop in case reptitions are requested - Removed atomic operation for cleanup recursion detection, since it is only trying to protect recursive invocations from itself - Using timerfd to wait for timeout instead of busy-waiting - Removed pid_popen(), pid_wait() and shell_int() - assert_exit_status() now also checks if status is -1, i.e. command failed - Moved _GNU_SOURCE macro from Makefile to C file - Removed -Werror from Makefile - Removed err(), fail(), pass() and info() macros - Replaced usage of READ and WRITE macros with STDIN_FILENO and STDOUT_FILENO, READ and WRITE macros removed - Removed some unnecessary comments - setup() is renamed to setup_children() - Informational message are made mandatory, verbose only affects output from sub-commands, partrt in particular - Test case name is now partrt_nohz_full partrt_nohz_full is a test case which checks whether nohz_full mode can achieve 0Hz ticks (if relevant extra patches has been applied) or 1Hz ticks (mainline kernel). Appropriate kernel configurations and kernel boot parameters needed are documented in testcases/kernel/partrt/README. Mats Liljegren (1): partrt_nohz_full: Introducing a new test case .gitmodules | 3 + README.kernel_config | 6 + runtest/partrt_nohz_full | 1 + testcases/kernel/Makefile | 1 + testcases/kernel/partrt/Makefile | 17 + testcases/kernel/partrt/README | 61 +++ testcases/kernel/partrt/nohz_full/.gitignore | 1 + testcases/kernel/partrt/nohz_full/Makefile | 23 + .../partrt/nohz_full/test_partrt_nohz_full.c | 551 ++++++++++++++++++++ utils/.gitignore | 3 + utils/Makefile | 7 + utils/rt-tools | 1 + 12 files changed, 675 insertions(+) create mode 100644 runtest/partrt_nohz_full create mode 100644 testcases/kernel/partrt/Makefile create mode 100644 testcases/kernel/partrt/README create mode 100644 testcases/kernel/partrt/nohz_full/.gitignore create mode 100644 testcases/kernel/partrt/nohz_full/Makefile create mode 100644 testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c create mode 160000 utils/rt-tools -- 1.7.10.4 ------------------------------------------------------------------------------ "Accelerate Dev Cycles with Automated Cross-Browser Testing - For FREE Instantly run your Selenium tests across 300+ browser/OS combos. Get unparalleled scalability from the best Selenium testing platform available. Simple to use. Nothing to install. Get started now for free." http://p.sf.net/sfu/SauceLabs _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
* [LTP] [PATCH] partrt_nohz_full: Introducing a new test case 2014-04-28 15:06 ` [LTP] [PATCH v2] " Mats Liljegren @ 2014-04-28 15:06 ` Mats Liljegren 2014-05-06 16:20 ` chrubis 2014-05-13 14:11 ` [LTP] [PATCH v3] " Mats Liljegren 1 sibling, 1 reply; 28+ messages in thread From: Mats Liljegren @ 2014-04-28 15:06 UTC (permalink / raw) To: ltp-list; +Cc: Frederic Weisbecker, Kevin Hilman This test case is intended to test the nohz_full kernel feature. See file testcases/kernel/partrt/README for more information. Signed-off-by: Mats Liljegren <mats.liljegren@enea.com> --- .gitmodules | 3 + README.kernel_config | 6 + runtest/partrt_nohz_full | 1 + testcases/kernel/Makefile | 1 + testcases/kernel/partrt/Makefile | 17 + testcases/kernel/partrt/README | 61 +++ testcases/kernel/partrt/nohz_full/.gitignore | 1 + testcases/kernel/partrt/nohz_full/Makefile | 23 + .../partrt/nohz_full/test_partrt_nohz_full.c | 551 ++++++++++++++++++++ utils/.gitignore | 3 + utils/Makefile | 7 + utils/rt-tools | 1 + 12 files changed, 675 insertions(+) create mode 100644 runtest/partrt_nohz_full create mode 100644 testcases/kernel/partrt/Makefile create mode 100644 testcases/kernel/partrt/README create mode 100644 testcases/kernel/partrt/nohz_full/.gitignore create mode 100644 testcases/kernel/partrt/nohz_full/Makefile create mode 100644 testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c create mode 160000 utils/rt-tools diff --git a/.gitmodules b/.gitmodules index 1c9e9c3..0461cbd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "testcases/kernel/mce-test"] path = testcases/kernel/mce-test url = git://git.kernel.org/pub/scm/linux/kernel/git/gong.chen/mce-test.git +[submodule "utils/rt-tools"] + path = utils/rt-tools + url = https://github.com/OpenEneaLinux/rt-tools diff --git a/README.kernel_config b/README.kernel_config index 54df0ad..5ebfe7e 100644 --- a/README.kernel_config +++ b/README.kernel_config @@ -311,3 +311,9 @@ And the version of packages must be 1.41.4 or above. For more information to build/install/run these tests, look through: ltp/testcases/kernel/fs/ext4-new-features/README --------------------------------- + +--------------------------------- +Enabling Kernel Configuration to run test partrt_nohz_full +--------------------------------- +CONFIG_NO_HZ_FULL=y +CONFIG_CPUSETS=y diff --git a/runtest/partrt_nohz_full b/runtest/partrt_nohz_full new file mode 100644 index 0000000..d84e192 --- /dev/null +++ b/runtest/partrt_nohz_full @@ -0,0 +1 @@ +partrt_nohz_full test_partrt_nohz_full -d 3000 diff --git a/testcases/kernel/Makefile b/testcases/kernel/Makefile index 6bffe79..cdd0406 100644 --- a/testcases/kernel/Makefile +++ b/testcases/kernel/Makefile @@ -47,6 +47,7 @@ SUBDIRS += connectors \ logging \ mem \ numa \ + partrt \ performance_counters \ pty \ sched \ diff --git a/testcases/kernel/partrt/Makefile b/testcases/kernel/partrt/Makefile new file mode 100644 index 0000000..120090e --- /dev/null +++ b/testcases/kernel/partrt/Makefile @@ -0,0 +1,17 @@ +# Copyright (C) 2014, Enea AB. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# + +top_srcdir ?= ../../.. + +include $(top_srcdir)/include/mk/env_pre.mk +include $(top_srcdir)/include/mk/generic_trunk_target.mk diff --git a/testcases/kernel/partrt/README b/testcases/kernel/partrt/README new file mode 100644 index 0000000..83bf239 --- /dev/null +++ b/testcases/kernel/partrt/README @@ -0,0 +1,61 @@ +partrt_nohz_full +================ + +This directory contains part_nohz_full test case for verifying absence of ticks +on nohz_full enabled CPUs. For more information about nohz_full and how to +enable this feature, see the following document in the kernel source code: + + Documentation/timers/NO_HZ.txt + +CPU partitioning is a way of isolating work with real-time requirements from +work without real-time requirements by assuring that the real-time work is done +on dedicated CPUs. This uses the kernel feature cgroup/cpuset. The goal is to +make sure that nohz_full mode can be enabled on these CPUs. + +Dependencies +------------ +CPU partitioning is performed by the partrt tool, which is available as a git +submodule. Before building, do the following from the LTP root directory to +include rt-tools in the build: + + git submodule update utils/rt-tools + +The following build time kernel configurations must be enabled: + + CONFIG_NO_HZ_FULL=y + CONFIG_CPUSETS=y + +These kernel configuration parameters are also documented in the +READ.kernel_config file in LTP root directory. + +The following kernel boot parameter needs to be set: + + nohz_full=<cpu list> + +There might be other parameters needed to actually get nohz_full working, but +this is largely dependent on architecture and kernel version. + +How to run +---------- + +The partrt_nohz_full test is run by runltp like so: + + ./runltp -f partrt_nohz_full + +To make life interesting, you can play with "-n" and "-i" options to runltp, to +generate some load in the non-realtime partition: + + ./runltp -n -i 5 -f partrt_nohz_full + +To get some more help with the cause of a failing test, do this as root before +running the test: + + echo 1 > /sys/kernel/debug/tracing/events/enable + +and then look at trace after test failed: + + cat /sys/kernel/debug/tracing/trace + +Since "count_ticks" also uses ftrace and will update tracing_cpumask to only +trace the nohz_full CPUs, the trace should be close to empty if the run was +successful. diff --git a/testcases/kernel/partrt/nohz_full/.gitignore b/testcases/kernel/partrt/nohz_full/.gitignore new file mode 100644 index 0000000..5848b21 --- /dev/null +++ b/testcases/kernel/partrt/nohz_full/.gitignore @@ -0,0 +1 @@ +/test_partrt_nohz_full diff --git a/testcases/kernel/partrt/nohz_full/Makefile b/testcases/kernel/partrt/nohz_full/Makefile new file mode 100644 index 0000000..5528dc0 --- /dev/null +++ b/testcases/kernel/partrt/nohz_full/Makefile @@ -0,0 +1,23 @@ +# +# Copyright (C) 2014, Enea AB. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +# the GNU General Public License for more details. +# + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk + +LDLIBS += -lrt + +MAKE_TARGETS := test_partrt_nohz_full + +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c b/testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c new file mode 100644 index 0000000..1343b71 --- /dev/null +++ b/testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c @@ -0,0 +1,551 @@ +/* + * Copyright (C) 2014, Enea AB. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + */ + +#define _GNU_SOURCE + +#include <errno.h> +#include <string.h> +#include <getopt.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/syscall.h> +#include <sys/stat.h> +#include <sys/timerfd.h> +#include <stdarg.h> +#include <inttypes.h> +#include <limits.h> +#include <time.h> +#include <sched.h> +#include <fcntl.h> + +#include <test.h> +#include <usctest.h> +#include <safe_macros.h> +#include <safe_stdio.h> + +const char *TCID = "partrt_nohz_full"; +const int TST_TOTAL = 1; + +/* Used for RT load */ +volatile int dummy_value; + +/* When true (1), all children will terminate */ +static volatile int time_is_up; + +/* Verbosity level chosen from program command line. + * 0 = No extra information from called sub-programs + * 1 = Request verbose information from called sub-programs + * 2 = Request more verbose inormation from called sub-programs. + */ +static int verbose; + +/* Name of application as given in argv[0] */ +static const char *appname; + +/* Boolean describing whether RT partition in cpuset needs to be created */ +int need_partrt_create = 0; + +/* RT load function prototype */ +typedef void (child_func)(void); + +/* Amount of stack for RT load threads */ +#define STACK_SZ (64 * 1024) + +/* Default path to cgroups/cpuset */ +#define CPUSET_ROOT "/sys/fs/cgroup/cpuset" + +/* Expected path to cpuset RT partition */ +#define CPUSET_RT_PATH CPUSET_ROOT "/rt" + +/* Expected path to RT partition tasks list file */ +#define CPUSET_RT_TASKS_FILE CPUSET_RT_PATH "/tasks" + +/* Expected path to RT partition CPU list file */ +#define CPUSET_RT_CPUS_FILE CPUSET_RT_PATH "/cpuset.cpus" + +/* File to look for to determine whether true 0Hz can be achieved */ +#define SCHED_TICK_MAX_FILE "/sys/kernel/debug/sched_tick_max_deferment" + +/* File containing a list of filesystems supported by the kernel */ +#define PROC_FILESYSTEMS_FILE "/proc/filesystems" + +static void cleanup(void); + +/* + * Check exit status value. + * If any signs of error is found, report error and abort testing. + */ +static void assert_exit_status(const char *cmd, int status) +{ + if (status == -1) + tst_brkm(TBROK | TERRNO, cleanup, + "%s: Could not execute command", cmd); + if (WIFSIGNALED(status)) + tst_brkm(TBROK, cleanup, + "%s: Child terminated unexpectedly due to signal nr %d", + cmd, + WTERMSIG(status)); + if (!WIFEXITED(status)) + tst_brkm(TBROK, cleanup, "%s: Child terminated unexpectedly", + cmd); + if (WEXITSTATUS(status) != 0) + tst_brkm(TBROK, cleanup, "%s: Child exited with exit status %d", + cmd, + WEXITSTATUS(status)); +} + +/* + * Execute given command using shell, ensure success (0) is returned. + */ +static void shell(const char *cmd, ...) +{ + char *cmd_buf; + va_list va; + + va_start(va, cmd); + if (vasprintf(&cmd_buf, cmd, va) == -1) + tst_brkm(TBROK, cleanup, + "%s: Not valid printf format, or out of memory", cmd); + va_end(va); + + tst_resm(TINFO, "%s: Executing", cmd_buf); + assert_exit_status(cmd_buf, system(cmd_buf)); + tst_resm(TINFO, "%s: Returns success", cmd_buf); + + free(cmd_buf); +} + +/* + * Convert string to unsigned long. + * Error prefix expressed as string. + */ +static unsigned long str_to_ulong( + const char *str, + const char *err_prefix + ) +{ + char *endptr; + unsigned long val; + val = strtoull(str, &endptr, 0); + if (endptr == str) + tst_brkm(TBROK, cleanup, + "%s: %s: Expected unsigned decimal value", + err_prefix, str); + if (*endptr != '\0') + tst_brkm(TBROK, cleanup, + "%s: %s: Garbage character '%c' found after decimal value", + err_prefix, str, *endptr); + + return val; +} + +/* + * Call a command using shell. + * Command line expressed as va_list. + * Returns the command's first line of output. + */ +static char *shell_str(char *dest, int size, const char *cmd, ...) +{ + int len; + char *cmd_buf; + FILE *file; + va_list va; + + /* Build command line */ + va_start(va, cmd); + if (vasprintf(&cmd_buf, cmd, va) == -1) + tst_brkm(TBROK, cleanup, + "%s: Not valid printf format, or out of memory", cmd); + va_end(va); + + /* Launch command */ + errno = 0; + file = popen(cmd_buf, "r"); + if (file == NULL) { + if (errno == 0) + tst_brkm(TBROK, cleanup, "%s: popen(): Out of memory", + cmd_buf); + else + tst_brkm(TBROK | TERRNO, cleanup, + "%s: popen() failed", cmd_buf); + } + + tst_resm(TINFO, "%s: Executing", cmd_buf); + + /* Read commands stdout */ + if (fgets(dest, size, file) == NULL) { + if (feof(file)) + tst_brkm(TBROK, cleanup, + "%s: Expected output from the command, but got nothing", + cmd_buf); + else + tst_brkm(TBROK | TERRNO, cleanup, + "%s: Could not read command output", + cmd_buf); + } + + /* Get rid of terminating newline, if any */ + len = strlen(dest); + if (dest[len-1] == '\n') + dest[len-1] = '\0'; + + /* Wait until command execution finish. + * Main reason for doing this even though we've got what we want is for + * better error detection. The alternative could be to let cleanup() + * handle it if process hasn't finished by then. */ + assert_exit_status(cmd_buf, pclose(file)); + + tst_resm(TINFO, "%s: Returns: %s", cmd_buf, dest); + + free(cmd_buf); + + return dest; +} + +static int child_entry(void *arg) +{ + (void) arg; + + while (!time_is_up) + dummy_value = rand(); + + return 0; +} + +/* + * Start a new thread executing the given function, on the given cpuset + * partition and CPU ID. Note that the CPU ID must be one of the + * available CPUs in the cpuset partition. + */ +static void launch_child(int cpu) +{ + char *stack = SAFE_MALLOC(cleanup, STACK_SZ); + int tid; + const int cpuset_tasks_fd = SAFE_OPEN(cleanup, CPUSET_RT_TASKS_FILE, + O_WRONLY); + char *tid_str; + cpu_set_t cpu_set; + + tid = ltp_clone(CLONE_VM, child_entry, NULL, STACK_SZ, stack); + if (tid == -1) + tst_brkm(TBROK | TERRNO, cleanup, "clone() failed"); + + /* Move child to RT partition */ + SAFE_ASPRINTF(cleanup, &tid_str, "%ld", (long) tid); + SAFE_WRITE(cleanup, 1, cpuset_tasks_fd, tid_str, strlen(tid_str)); + free(tid_str); + SAFE_CLOSE(cleanup, cpuset_tasks_fd); + + /* Move child to indicated CPU within the RT partition */ + CPU_ZERO(&cpu_set); + CPU_SET(cpu, &cpu_set); + if (sched_setaffinity(tid, sizeof(cpu_set), &cpu_set) < 0) + tst_brkm(TBROK | TERRNO, cleanup, + "pid %u: sched_setaffinity() failed", + tid); + + tst_resm(TINFO, "pid %d: Starting RT load on cpu %d", + tid, cpu); +} + +static void cleanup(void) +{ + static int cleanup_entered; + + pid_t pid; + int status; + + if (cleanup_entered) + return; /* Called from cleanup() */ + + cleanup_entered = 1; + + tst_resm(TINFO, "Cleanup: Terminating children"); + + time_is_up = 1; + + do { + pid = wait(&status); + if (pid != -1) { + char cmd[64]; + snprintf(cmd, sizeof(cmd), "Pid %lu", + (unsigned long) pid); + assert_exit_status(cmd, status); + tst_resm(TINFO, + "Cleanup: %s: Has terminated successfully", + cmd); + } + } while (pid != -1); + + if (errno != ECHILD) + tst_brkm(TBROK | TERRNO, NULL, "Cleanup: wait() failed"); + + if (need_partrt_create) + shell("partrt undo"); + + tst_resm(TINFO, "Cleanup: Done"); +} + +static unsigned long determine_nohz_mask(void) +{ + int range_first; + int range_last; + int bit; + unsigned long mask = 0; + FILE *stream = SAFE_FOPEN(cleanup, CPUSET_RT_CPUS_FILE, "r"); + int nr_matches; + + for (nr_matches = fscanf(stream, "%d-%d", &range_first, &range_last); + nr_matches > 0; + nr_matches = fscanf(stream, ",%d-%d", &range_first, &range_last)) { + if (nr_matches == 1) + range_last = range_first; + + /* Set all bits in range */ + for (bit = range_first; bit <= range_last; bit++) + mask |= (1 << bit); + } + + SAFE_FCLOSE(cleanup, stream); + + if (nr_matches == -1) + tst_brkm(TBROK | TERRNO, cleanup, "%s: fscanf() failed", + CPUSET_RT_CPUS_FILE); + + return mask; +} + +static void tool_available(char *tool_name, const char *env_path) +{ + int success = 0; + + if (strchr(tool_name, '/') != NULL) { + if (access(tool_name, X_OK) == 0) + success = 1; + } else { + char full_path[PATH_MAX]; + const char *curr; + char * const alloced_path = strdup(env_path); + char *next = alloced_path; + + for (curr = strsep(&next, ":"); + curr != NULL; + curr = strsep(&next, ":")) { + if (*curr == '\0') + curr = "."; + if (snprintf(full_path, sizeof(full_path), + "%s/%s", curr, tool_name + ) >= (int)sizeof(full_path)) + continue; + if (access(full_path, X_OK) == 0) { + success = 1; + break; + } + } + free(alloced_path); + } + + if (!success) + tst_brkm(TCONF, cleanup, + "%s tool not found, skipping test. Use 'git submodule update utils/rt-tools' to include needed tools in the build.", + tool_name); +} + +static void tools_check(void) +{ + const char *env_path = getenv("PATH"); + + if (env_path == NULL) + env_path = ""; + + tool_available("partrt", env_path); + tool_available("list2mask", env_path); + tool_available("count_ticks", env_path); +} + +static void cpuset_check(void) +{ + FILE * const stream = SAFE_FOPEN(cleanup, PROC_FILESYSTEMS_FILE, "r"); + char *name; + + while (fscanf(stream, "%as", &name) > 0) { + if (strcmp(name, "cpuset") == 0) { + free(name); + return; + } + free(name); + } + + tst_brkm(TCONF, cleanup, "CPUSET not configured in kernel"); +} + +/* + * Setup and return number of child threads + */ +static int setup_children(void) +{ + unsigned long nohz_mask; + int bit; + int nr_children = 0; + + time_is_up = 0; + + tools_check(); + tst_require_root(NULL); + cpuset_check(); + + if (access(CPUSET_RT_PATH, F_OK) == -1) + need_partrt_create = 1; + + if (need_partrt_create) + shell("partrt create $(list2mask --nohz)"); + + nohz_mask = determine_nohz_mask(); + tst_resm(TINFO, "Nohz CPU mask: %#lx", nohz_mask); + + for (bit = 0; bit < (int) (sizeof(nohz_mask) * 8); bit++) { + if (nohz_mask & (1lu << bit)) { + launch_child(bit); + nr_children++; + } + } + + tst_resm(TINFO, "All children started"); + return nr_children; +} + +/* + * Perform tick measurement for the given number of seconds. + */ +static void test(time_t duration, unsigned nr_children) +{ + uint64_t nr_ticks; + time_t time_finished; + /* If sched_tick_max_deferment patch has been applied, expect that the + * partitioning has disabled ticks completely. Otherwise, expect + * 1Hz ticks */ + const int expect_0hz = + access(SCHED_TICK_MAX_FILE, F_OK) == 0; + const time_t current_time = time(NULL); + + const uint64_t expected_ticks = expect_0hz ? 0 : duration * nr_children; + char val_str[64]; + static const char count_ticks_end_cmd[] = + "count_ticks --cpu rt --batch --end"; + int timer_fd; + struct itimerspec timeout = { {0}, {0} }; + uint64_t nr_timeouts; + + shell("count_ticks --cpu rt --start"); + + time_finished = time(NULL) + duration; + + tst_resm(TINFO, "Execution started: %s", ctime(¤t_time)); + tst_resm(TINFO, "Execution ends : %s", ctime(&time_finished)); + + timer_fd = timerfd_create(CLOCK_MONOTONIC, 0); + timeout.it_value.tv_sec = duration; + timerfd_settime(timer_fd, 0, &timeout, NULL); + + /* Wait for timeout */ + SAFE_READ(cleanup, 1, timer_fd, &nr_timeouts, sizeof(nr_timeouts)); + if (nr_timeouts != 1) + tst_brkm(TBROK, cleanup, + "Multiple timeouts when single timeout was requested"); + + shell_str(val_str, sizeof(val_str), count_ticks_end_cmd); + nr_ticks = str_to_ulong(val_str, count_ticks_end_cmd); + + if (expect_0hz) { + if (nr_ticks != 0) + tst_resm(TFAIL, "Expected no ticks, but got %" PRIu64, + nr_ticks); + else + tst_resm(TPASS, "No ticks occurred"); + } else { + if (nr_ticks > expected_ticks) + tst_resm(TFAIL, + "Expected maximum %" PRIu64 + " ticks, but got %" PRIu64, + expected_ticks, nr_ticks); + else + tst_resm(TPASS, + "%" PRIu64 + " ticks occurred, which was expected", + nr_ticks); + } +} + +static void usage(void) +{ + printf( + "Usage: %s [options]\n" + " %s --help\n" + "Test whether a CPU can be isolated and made tickless even under load.\n" + "\n" + " -h Display this usage text and exit.\n" + " -v <level> Set verbose level, 0 = default.\n" + " -d <secs> Number of seconds to run the test.\n" + , + appname, appname + ); + + exit(1); +} + +int main(int argc, char *argv[]) +{ + long duration = 0; + int lc; + unsigned int nr_children; + char *error_msg; + char *verbose_str = NULL; + char *duration_str = NULL; + + const option_t options[] = { + {"v:", NULL, &verbose_str}, + {"d:", NULL, &duration_str}, + {NULL, NULL, NULL} + }; + + verbose = 0; + appname = basename(argv[0]); + + error_msg = parse_opts(argc, argv, options, usage); + if (error_msg != NULL) + tst_brkm(TBROK, NULL, "Error parsing command line: %s", + error_msg); + + if (verbose_str != NULL) + verbose = str_to_ulong(verbose_str, "-v"); + + if (duration_str == NULL) + tst_brkm(TBROK, cleanup, + "No duration specified, nothing to do"); + + duration = (long) str_to_ulong(duration_str, "-d"); + + tst_resm(TINFO, "%s: Compiled %s %s", __FILE__, __DATE__, __TIME__); + + nr_children = setup_children(); + + for (lc = 0; TEST_LOOPING(lc); lc++) + test(duration, nr_children); + + cleanup(); + tst_exit(); + + /* Should not end up here */ + return 1; +} diff --git a/utils/.gitignore b/utils/.gitignore index a582748..f63e51a 100644 --- a/utils/.gitignore +++ b/utils/.gitignore @@ -48,3 +48,6 @@ /sctp/func_tests/test_tcp_style_v6 /sctp/func_tests/test_timetolive /sctp/func_tests/test_timetolive_v6 +/count_ticks +/list2mask +/partrt diff --git a/utils/Makefile b/utils/Makefile index 1508b35..8c2fc7e 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -22,7 +22,11 @@ top_srcdir ?= .. include $(top_srcdir)/include/mk/env_pre.mk +ifneq ($(wildcard rt-tools),) +MAKE_TARGETS += ffsb partrt list2mask count_ticks +else MAKE_TARGETS += ffsb +endif FFSBDIR := ffsb-6.0-rc2 FILTER_OUT_DIRS := $(FFSBDIR) @@ -35,6 +39,9 @@ $(FFSB): $(abs_srcdir)/$(FFSBDIR) ffsb: $(FFSB) cp $(FFSB) ffsb +partrt list2mask count_ticks: + cp rt-tools/install/bin/$@ $@ + trunk-all: $(FFSB) trunk-clean:: | ffsb-clean diff --git a/utils/rt-tools b/utils/rt-tools new file mode 160000 index 0000000..f75b334 --- /dev/null +++ b/utils/rt-tools @@ -0,0 +1 @@ +Subproject commit f75b334922a2243d0b757c0627c6f1c8440818c0 -- 1.7.10.4 ------------------------------------------------------------------------------ "Accelerate Dev Cycles with Automated Cross-Browser Testing - For FREE Instantly run your Selenium tests across 300+ browser/OS combos. Get unparalleled scalability from the best Selenium testing platform available. Simple to use. Nothing to install. Get started now for free." http://p.sf.net/sfu/SauceLabs _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [LTP] [PATCH] partrt_nohz_full: Introducing a new test case 2014-04-28 15:06 ` [LTP] [PATCH] " Mats Liljegren @ 2014-05-06 16:20 ` chrubis [not found] ` <20140507132016.40361a0a@mats-desktop> 0 siblings, 1 reply; 28+ messages in thread From: chrubis @ 2014-05-06 16:20 UTC (permalink / raw) To: Mats Liljegren; +Cc: ltp-list, Kevin Hilman, Frederic Weisbecker Hi! > +Dependencies > +------------ > +CPU partitioning is performed by the partrt tool, which is available as a git > +submodule. Before building, do the following from the LTP root directory to > +include rt-tools in the build: > + > + git submodule update utils/rt-tools I think that you have to do git submodule init prior to this (at least if you do this right after you cloned git repository). > +The following build time kernel configurations must be enabled: > + > + CONFIG_NO_HZ_FULL=y > + CONFIG_CPUSETS=y > + > +These kernel configuration parameters are also documented in the > +READ.kernel_config file in LTP root directory. > + > +The following kernel boot parameter needs to be set: > + > + nohz_full=<cpu list> > + > +There might be other parameters needed to actually get nohz_full working, but > +this is largely dependent on architecture and kernel version. > + > +How to run > +---------- > + > +The partrt_nohz_full test is run by runltp like so: > + > + ./runltp -f partrt_nohz_full > + > +To make life interesting, you can play with "-n" and "-i" options to runltp, to > +generate some load in the non-realtime partition: > + > + ./runltp -n -i 5 -f partrt_nohz_full > + > +To get some more help with the cause of a failing test, do this as root before > +running the test: > + > + echo 1 > /sys/kernel/debug/tracing/events/enable > + > +and then look at trace after test failed: > + > + cat /sys/kernel/debug/tracing/trace > + > +Since "count_ticks" also uses ftrace and will update tracing_cpumask to only > +trace the nohz_full CPUs, the trace should be close to empty if the run was > +successful. > diff --git a/testcases/kernel/partrt/nohz_full/.gitignore b/testcases/kernel/partrt/nohz_full/.gitignore > new file mode 100644 > index 0000000..5848b21 > --- /dev/null > +++ b/testcases/kernel/partrt/nohz_full/.gitignore > @@ -0,0 +1 @@ > +/test_partrt_nohz_full > diff --git a/testcases/kernel/partrt/nohz_full/Makefile b/testcases/kernel/partrt/nohz_full/Makefile > new file mode 100644 > index 0000000..5528dc0 > --- /dev/null > +++ b/testcases/kernel/partrt/nohz_full/Makefile > @@ -0,0 +1,23 @@ > +# > +# Copyright (C) 2014, Enea AB. > +# > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; either version 2 of the License, or > +# (at your option) any later version. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See > +# the GNU General Public License for more details. > +# > + > +top_srcdir ?= ../../../.. > + > +include $(top_srcdir)/include/mk/testcases.mk > + > +LDLIBS += -lrt > + > +MAKE_TARGETS := test_partrt_nohz_full > + > +include $(top_srcdir)/include/mk/generic_leaf_target.mk > diff --git a/testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c b/testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c > new file mode 100644 > index 0000000..1343b71 > --- /dev/null > +++ b/testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c > @@ -0,0 +1,551 @@ > +/* > + * Copyright (C) 2014, Enea AB. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See > + * the GNU General Public License for more details. > + */ > + > +#define _GNU_SOURCE > + > +#include <errno.h> > +#include <string.h> > +#include <getopt.h> > +#include <sys/types.h> > +#include <sys/wait.h> > +#include <sys/syscall.h> > +#include <sys/stat.h> > +#include <sys/timerfd.h> > +#include <stdarg.h> > +#include <inttypes.h> > +#include <limits.h> > +#include <time.h> > +#include <sched.h> > +#include <fcntl.h> > + > +#include <test.h> > +#include <usctest.h> > +#include <safe_macros.h> > +#include <safe_stdio.h> > + > +const char *TCID = "partrt_nohz_full"; > +const int TST_TOTAL = 1; > + > +/* Used for RT load */ > +volatile int dummy_value; > + > +/* When true (1), all children will terminate */ > +static volatile int time_is_up; > + > +/* Verbosity level chosen from program command line. > + * 0 = No extra information from called sub-programs > + * 1 = Request verbose information from called sub-programs > + * 2 = Request more verbose inormation from called sub-programs. > + */ > +static int verbose; The verbosity level doesn't seem to be used anywhere (apart from being set while parsing command line options). > +/* Name of application as given in argv[0] */ > +static const char *appname; > + > +/* Boolean describing whether RT partition in cpuset needs to be created */ > +int need_partrt_create = 0; > + > +/* RT load function prototype */ > +typedef void (child_func)(void); > + > +/* Amount of stack for RT load threads */ > +#define STACK_SZ (64 * 1024) > + > +/* Default path to cgroups/cpuset */ > +#define CPUSET_ROOT "/sys/fs/cgroup/cpuset" > + > +/* Expected path to cpuset RT partition */ > +#define CPUSET_RT_PATH CPUSET_ROOT "/rt" > + > +/* Expected path to RT partition tasks list file */ > +#define CPUSET_RT_TASKS_FILE CPUSET_RT_PATH "/tasks" > + > +/* Expected path to RT partition CPU list file */ > +#define CPUSET_RT_CPUS_FILE CPUSET_RT_PATH "/cpuset.cpus" > + > +/* File to look for to determine whether true 0Hz can be achieved */ > +#define SCHED_TICK_MAX_FILE "/sys/kernel/debug/sched_tick_max_deferment" > + > +/* File containing a list of filesystems supported by the kernel */ > +#define PROC_FILESYSTEMS_FILE "/proc/filesystems" > + > +static void cleanup(void); > + > +/* > + * Check exit status value. > + * If any signs of error is found, report error and abort testing. > + */ > +static void assert_exit_status(const char *cmd, int status) > +{ > + if (status == -1) > + tst_brkm(TBROK | TERRNO, cleanup, > + "%s: Could not execute command", cmd); > + if (WIFSIGNALED(status)) > + tst_brkm(TBROK, cleanup, > + "%s: Child terminated unexpectedly due to signal nr %d", > + cmd, > + WTERMSIG(status)); I think that it's more readbale to add the curly braces around block that spawns to multiple lines even if it's technically one function call. > + if (!WIFEXITED(status)) > + tst_brkm(TBROK, cleanup, "%s: Child terminated unexpectedly", > + cmd); > + if (WEXITSTATUS(status) != 0) > + tst_brkm(TBROK, cleanup, "%s: Child exited with exit status %d", > + cmd, > + WEXITSTATUS(status)); > +} > + > +/* > + * Execute given command using shell, ensure success (0) is returned. > + */ > +static void shell(const char *cmd, ...) > +{ > + char *cmd_buf; > + va_list va; > + > + va_start(va, cmd); > + if (vasprintf(&cmd_buf, cmd, va) == -1) > + tst_brkm(TBROK, cleanup, > + "%s: Not valid printf format, or out of memory", cmd); > + va_end(va); > + > + tst_resm(TINFO, "%s: Executing", cmd_buf); > + assert_exit_status(cmd_buf, system(cmd_buf)); > + tst_resm(TINFO, "%s: Returns success", cmd_buf); > + > + free(cmd_buf); > +} > + > +/* > + * Convert string to unsigned long. > + * Error prefix expressed as string. > + */ > +static unsigned long str_to_ulong( > + const char *str, > + const char *err_prefix > + ) Why not just: static unsigned long str_to_ulong(const char *str, const char *err_prefix) As far as I can the line fits into 80 chars. > +{ > + char *endptr; > + unsigned long val; > + val = strtoull(str, &endptr, 0); > + if (endptr == str) > + tst_brkm(TBROK, cleanup, > + "%s: %s: Expected unsigned decimal value", > + err_prefix, str); > + if (*endptr != '\0') > + tst_brkm(TBROK, cleanup, > + "%s: %s: Garbage character '%c' found after decimal value", > + err_prefix, str, *endptr); > + > + return val; > +} > + > +/* > + * Call a command using shell. > + * Command line expressed as va_list. > + * Returns the command's first line of output. > + */ > +static char *shell_str(char *dest, int size, const char *cmd, ...) > +{ > + int len; > + char *cmd_buf; > + FILE *file; > + va_list va; > + > + /* Build command line */ > + va_start(va, cmd); > + if (vasprintf(&cmd_buf, cmd, va) == -1) > + tst_brkm(TBROK, cleanup, > + "%s: Not valid printf format, or out of memory", cmd); > + va_end(va); > + > + /* Launch command */ > + errno = 0; > + file = popen(cmd_buf, "r"); > + if (file == NULL) { > + if (errno == 0) > + tst_brkm(TBROK, cleanup, "%s: popen(): Out of memory", > + cmd_buf); > + else > + tst_brkm(TBROK | TERRNO, cleanup, > + "%s: popen() failed", cmd_buf); > + } This may be worth of SAFE_POPEN(), I can add it if you want. > + tst_resm(TINFO, "%s: Executing", cmd_buf); > + > + /* Read commands stdout */ > + if (fgets(dest, size, file) == NULL) { > + if (feof(file)) > + tst_brkm(TBROK, cleanup, > + "%s: Expected output from the command, but got nothing", > + cmd_buf); > + else > + tst_brkm(TBROK | TERRNO, cleanup, > + "%s: Could not read command output", > + cmd_buf); > + } > + > + /* Get rid of terminating newline, if any */ > + len = strlen(dest); > + if (dest[len-1] == '\n') > + dest[len-1] = '\0'; > + > + /* Wait until command execution finish. > + * Main reason for doing this even though we've got what we want is for > + * better error detection. The alternative could be to let cleanup() > + * handle it if process hasn't finished by then. */ > + assert_exit_status(cmd_buf, pclose(file)); > + > + tst_resm(TINFO, "%s: Returns: %s", cmd_buf, dest); > + > + free(cmd_buf); > + > + return dest; > +} > + > +static int child_entry(void *arg) > +{ > + (void) arg; > + > + while (!time_is_up) > + dummy_value = rand(); > + > + return 0; > +} > + > +/* > + * Start a new thread executing the given function, on the given cpuset > + * partition and CPU ID. Note that the CPU ID must be one of the > + * available CPUs in the cpuset partition. > + */ > +static void launch_child(int cpu) > +{ > + char *stack = SAFE_MALLOC(cleanup, STACK_SZ); > + int tid; > + const int cpuset_tasks_fd = SAFE_OPEN(cleanup, CPUSET_RT_TASKS_FILE, > + O_WRONLY); > + char *tid_str; > + cpu_set_t cpu_set; > + > + tid = ltp_clone(CLONE_VM, child_entry, NULL, STACK_SZ, stack); > + if (tid == -1) > + tst_brkm(TBROK | TERRNO, cleanup, "clone() failed"); > + > + /* Move child to RT partition */ > + SAFE_ASPRINTF(cleanup, &tid_str, "%ld", (long) tid); > + SAFE_WRITE(cleanup, 1, cpuset_tasks_fd, tid_str, strlen(tid_str)); > + > + free(tid_str); > + SAFE_CLOSE(cleanup, cpuset_tasks_fd); SAFE_FILE_PRINTF(cleanup, CPUSET_RT_TASKS_FILE, "%ld", (long)tid) ? Or is there a good reason to use open(), asprintf(), write() and close() instead? > + > + /* Move child to indicated CPU within the RT partition */ > + CPU_ZERO(&cpu_set); > + CPU_SET(cpu, &cpu_set); > + if (sched_setaffinity(tid, sizeof(cpu_set), &cpu_set) < 0) > + tst_brkm(TBROK | TERRNO, cleanup, > + "pid %u: sched_setaffinity() failed", > + tid); > + > + tst_resm(TINFO, "pid %d: Starting RT load on cpu %d", > + tid, cpu); > +} > + > +static void cleanup(void) > +{ > + static int cleanup_entered; > + > + pid_t pid; > + int status; > + > + if (cleanup_entered) > + return; /* Called from cleanup() */ > + > + cleanup_entered = 1; Why is this still needed? It shouldn't be if only parent process uses the tst_* interface. > + tst_resm(TINFO, "Cleanup: Terminating children"); > + > + time_is_up = 1; > + > + do { > + pid = wait(&status); > + if (pid != -1) { > + char cmd[64]; > + snprintf(cmd, sizeof(cmd), "Pid %lu", > + (unsigned long) pid); > + assert_exit_status(cmd, status); > + tst_resm(TINFO, > + "Cleanup: %s: Has terminated successfully", > + cmd); > + } > + } while (pid != -1); > + > + if (errno != ECHILD) > + tst_brkm(TBROK | TERRNO, NULL, "Cleanup: wait() failed"); > + > + if (need_partrt_create) > + shell("partrt undo"); > + > + tst_resm(TINFO, "Cleanup: Done"); > +} > + > +static unsigned long determine_nohz_mask(void) > +{ > + int range_first; > + int range_last; > + int bit; > + unsigned long mask = 0; > + FILE *stream = SAFE_FOPEN(cleanup, CPUSET_RT_CPUS_FILE, "r"); > + int nr_matches; > + > + for (nr_matches = fscanf(stream, "%d-%d", &range_first, &range_last); > + nr_matches > 0; > + nr_matches = fscanf(stream, ",%d-%d", &range_first, &range_last)) { > + if (nr_matches == 1) > + range_last = range_first; > + > + /* Set all bits in range */ > + for (bit = range_first; bit <= range_last; bit++) > + mask |= (1 << bit); Are you sure that this would not overflow? I guess that it depends on how you have partitioned your CPUs. > + } > + > + SAFE_FCLOSE(cleanup, stream); > + > + if (nr_matches == -1) > + tst_brkm(TBROK | TERRNO, cleanup, "%s: fscanf() failed", > + CPUSET_RT_CPUS_FILE); > + > + return mask; > +} > + > +static void tool_available(char *tool_name, const char *env_path) > +{ > + int success = 0; > + > + if (strchr(tool_name, '/') != NULL) { > + if (access(tool_name, X_OK) == 0) > + success = 1; > + } else { > + char full_path[PATH_MAX]; > + const char *curr; > + char * const alloced_path = strdup(env_path); > + char *next = alloced_path; > + > + for (curr = strsep(&next, ":"); > + curr != NULL; > + curr = strsep(&next, ":")) { > + if (*curr == '\0') > + curr = "."; > + if (snprintf(full_path, sizeof(full_path), > + "%s/%s", curr, tool_name > + ) >= (int)sizeof(full_path)) > + continue; > + if (access(full_path, X_OK) == 0) { > + success = 1; > + break; > + } > + } > + free(alloced_path); > + } > + > + if (!success) > + tst_brkm(TCONF, cleanup, > + "%s tool not found, skipping test. Use 'git submodule update utils/rt-tools' to include needed tools in the build.", > + tool_name); > +} > + > +static void tools_check(void) > +{ > + const char *env_path = getenv("PATH"); > + > + if (env_path == NULL) > + env_path = ""; > + > + tool_available("partrt", env_path); > + tool_available("list2mask", env_path); > + tool_available("count_ticks", env_path); We have tst_get_path() that could be used as: char buf[2048]; if (!tst_get_path("partrt", buf, sizeof(buf))) tst_brkm(TCONF, cleanup, "Tool partrt not available"); > +} > + > +static void cpuset_check(void) > +{ > + FILE * const stream = SAFE_FOPEN(cleanup, PROC_FILESYSTEMS_FILE, "r"); > * > + char *name; > + > + while (fscanf(stream, "%as", &name) > 0) { > + if (strcmp(name, "cpuset") == 0) { > + free(name); > + return; > + } > + free(name); > + } > + > + tst_brkm(TCONF, cleanup, "CPUSET not configured in kernel"); > +} > + > +/* > + * Setup and return number of child threads > + */ > +static int setup_children(void) > +{ > + unsigned long nohz_mask; > + int bit; > + int nr_children = 0; > + > + time_is_up = 0; > + > + tools_check(); > + tst_require_root(NULL); > + cpuset_check(); > + > + if (access(CPUSET_RT_PATH, F_OK) == -1) > + need_partrt_create = 1; > + > + if (need_partrt_create) > + shell("partrt create $(list2mask --nohz)"); > + > + nohz_mask = determine_nohz_mask(); > + tst_resm(TINFO, "Nohz CPU mask: %#lx", nohz_mask); > + > + for (bit = 0; bit < (int) (sizeof(nohz_mask) * 8); bit++) { > + if (nohz_mask & (1lu << bit)) { > + launch_child(bit); > + nr_children++; > + } > + } > + > + tst_resm(TINFO, "All children started"); > + return nr_children; > +} > + > +/* > + * Perform tick measurement for the given number of seconds. > + */ > +static void test(time_t duration, unsigned nr_children) > +{ > + uint64_t nr_ticks; > + time_t time_finished; > + /* If sched_tick_max_deferment patch has been applied, expect that the > + * partitioning has disabled ticks completely. Otherwise, expect > + * 1Hz ticks */ > + const int expect_0hz = > + access(SCHED_TICK_MAX_FILE, F_OK) == 0; > + const time_t current_time = time(NULL); > + > + const uint64_t expected_ticks = expect_0hz ? 0 : duration * nr_children; > + char val_str[64]; > + static const char count_ticks_end_cmd[] = > + "count_ticks --cpu rt --batch --end"; > + int timer_fd; > + struct itimerspec timeout = { {0}, {0} }; > + uint64_t nr_timeouts; > + > + shell("count_ticks --cpu rt --start"); > + > + time_finished = time(NULL) + duration; > + > + tst_resm(TINFO, "Execution started: %s", ctime(¤t_time)); > + tst_resm(TINFO, "Execution ends : %s", ctime(&time_finished)); What is this information good for? As far as I can see it says how long it took to prepare for the actual test. > + timer_fd = timerfd_create(CLOCK_MONOTONIC, 0); > + timeout.it_value.tv_sec = duration; > + timerfd_settime(timer_fd, 0, &timeout, NULL); > + > + /* Wait for timeout */ > + SAFE_READ(cleanup, 1, timer_fd, &nr_timeouts, sizeof(nr_timeouts)); > + if (nr_timeouts != 1) > + tst_brkm(TBROK, cleanup, > + "Multiple timeouts when single timeout was requested"); > + > + shell_str(val_str, sizeof(val_str), count_ticks_end_cmd); > + nr_ticks = str_to_ulong(val_str, count_ticks_end_cmd); > + > + if (expect_0hz) { > + if (nr_ticks != 0) > + tst_resm(TFAIL, "Expected no ticks, but got %" PRIu64, > + nr_ticks); > + else > + tst_resm(TPASS, "No ticks occurred"); > + } else { > + if (nr_ticks > expected_ticks) > + tst_resm(TFAIL, > + "Expected maximum %" PRIu64 > + " ticks, but got %" PRIu64, > + expected_ticks, nr_ticks); > + else > + tst_resm(TPASS, > + "%" PRIu64 > + " ticks occurred, which was expected", > + nr_ticks); > + } > +} > + > +static void usage(void) > +{ > + printf( > + "Usage: %s [options]\n" > + " %s --help\n" > + "Test whether a CPU can be isolated and made tickless even under load.\n" > + "\n" > + " -h Display this usage text and exit.\n" > + " -v <level> Set verbose level, 0 = default.\n" > + " -d <secs> Number of seconds to run the test.\n" > + , > + appname, appname > + ); > + > + exit(1); The library exits after usage is printed, the exit(1) here will not be reached. > +} > + > +int main(int argc, char *argv[]) > +{ > + long duration = 0; > + int lc; > + unsigned int nr_children; > + char *error_msg; > + char *verbose_str = NULL; > + char *duration_str = NULL; > + > + const option_t options[] = { > + {"v:", NULL, &verbose_str}, > + {"d:", NULL, &duration_str}, > + {NULL, NULL, NULL} > + }; > + > + verbose = 0; > + appname = basename(argv[0]); > + > + error_msg = parse_opts(argc, argv, options, usage); > + if (error_msg != NULL) > + tst_brkm(TBROK, NULL, "Error parsing command line: %s", > + error_msg); > + > + if (verbose_str != NULL) > + verbose = str_to_ulong(verbose_str, "-v"); > + > + if (duration_str == NULL) > + tst_brkm(TBROK, cleanup, > + "No duration specified, nothing to do"); > + > + duration = (long) str_to_ulong(duration_str, "-d"); > + > + tst_resm(TINFO, "%s: Compiled %s %s", __FILE__, __DATE__, __TIME__); I would say that this is not very useful information, or is it? > + nr_children = setup_children(); > + > + for (lc = 0; TEST_LOOPING(lc); lc++) > + test(duration, nr_children); > + > + cleanup(); > + tst_exit(); > + > + /* Should not end up here */ > + return 1; Remove the return from here. The tst_exit() is marked as __attribute__((noreturn)) and the compiler knows that you cannot get here. > +} > diff --git a/utils/.gitignore b/utils/.gitignore > index a582748..f63e51a 100644 > --- a/utils/.gitignore > +++ b/utils/.gitignore > @@ -48,3 +48,6 @@ > /sctp/func_tests/test_tcp_style_v6 > /sctp/func_tests/test_timetolive > /sctp/func_tests/test_timetolive_v6 > +/count_ticks > +/list2mask > +/partrt > diff --git a/utils/Makefile b/utils/Makefile > index 1508b35..8c2fc7e 100644 > --- a/utils/Makefile > +++ b/utils/Makefile > @@ -22,7 +22,11 @@ top_srcdir ?= .. > > include $(top_srcdir)/include/mk/env_pre.mk > > +ifneq ($(wildcard rt-tools),) > +MAKE_TARGETS += ffsb partrt list2mask count_ticks > +else > MAKE_TARGETS += ffsb > +endif This would be better as: MAKE_TARGETS += ffsb ifneq ($(wildcard rt-tools),) MAKE_TARGETS += partrt list2mask count_ticks endif because otherwise we would need to add any new tool into two places which is prone to mistakes. > FFSBDIR := ffsb-6.0-rc2 > FILTER_OUT_DIRS := $(FFSBDIR) > @@ -35,6 +39,9 @@ $(FFSB): $(abs_srcdir)/$(FFSBDIR) > ffsb: $(FFSB) > cp $(FFSB) ffsb > > +partrt list2mask count_ticks: > + cp rt-tools/install/bin/$@ $@ > + > trunk-all: $(FFSB) > > trunk-clean:: | ffsb-clean > diff --git a/utils/rt-tools b/utils/rt-tools > new file mode 160000 > index 0000000..f75b334 > --- /dev/null > +++ b/utils/rt-tools > @@ -0,0 +1 @@ > +Subproject commit f75b334922a2243d0b757c0627c6f1c8440818c0 > -- > 1.7.10.4 > > > ------------------------------------------------------------------------------ > "Accelerate Dev Cycles with Automated Cross-Browser Testing - For FREE > Instantly run your Selenium tests across 300+ browser/OS combos. Get > unparalleled scalability from the best Selenium testing platform available. > Simple to use. Nothing to install. Get started now for free." > http://p.sf.net/sfu/SauceLabs > _______________________________________________ > Ltp-list mailing list > Ltp-list@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/ltp-list -- Cyril Hrubis chrubis@suse.cz ------------------------------------------------------------------------------ Is your legacy SCM system holding you back? Join Perforce May 7 to find out: • 3 signs your SCM is hindering your productivity • Requirements for releasing software faster • Expert tips and advice for migrating your SCM now http://p.sf.net/sfu/perforce _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
[parent not found: <20140507132016.40361a0a@mats-desktop>]
* Re: [LTP] [PATCH] partrt_nohz_full: Introducing a new test case [not found] ` <20140507132016.40361a0a@mats-desktop> @ 2014-05-07 12:17 ` chrubis 0 siblings, 0 replies; 28+ messages in thread From: chrubis @ 2014-05-07 12:17 UTC (permalink / raw) To: Mats Liljegren; +Cc: ltp-list, Kevin Hilman, Frederic Weisbecker Hi! > > > + /* Launch command */ > > > + errno = 0; > > > + file = popen(cmd_buf, "r"); > > > + if (file == NULL) { > > > + if (errno == 0) > > > + tst_brkm(TBROK, cleanup, "%s: popen(): Out > > > of memory", > > > + cmd_buf); > > > + else > > > + tst_brkm(TBROK | TERRNO, cleanup, > > > + "%s: popen() failed", cmd_buf); > > > + } > > > > This may be worth of SAFE_POPEN(), I can add it if you want. > > Go ahead. I'll update my code when you're done. Ok. > > > + /* Move child to RT partition */ > > > + SAFE_ASPRINTF(cleanup, &tid_str, "%ld", (long) tid); > > > + SAFE_WRITE(cleanup, 1, cpuset_tasks_fd, tid_str, > > > strlen(tid_str)); + > > > + free(tid_str); > > > + SAFE_CLOSE(cleanup, cpuset_tasks_fd); > > > > SAFE_FILE_PRINTF(cleanup, CPUSET_RT_TASKS_FILE, "%ld", > > (long)tid) ? > > > > Or is there a good reason to use open(), asprintf(), write() > > and close() instead? > > Well, printf() by itself will do no file updates, but I replaced > open(), asprintf(), write(), free() and close() with fopen(), fprintf() > and fclose(). You lost me here. Have you seen the code for the SAFE_FILE_PRINTF()? What it does is is fopen(), fprintf() and fclose(). It was designed to simplify writing to various kernel pseudo filesystems to one call. > > > + for (nr_matches = fscanf(stream, "%d-%d", &range_first, > > > &range_last); > > > + nr_matches > 0; > > > + nr_matches = fscanf(stream, ",%d-%d", &range_first, > > > &range_last)) { > > > + if (nr_matches == 1) > > > + range_last = range_first; > > > + > > > + /* Set all bits in range */ > > > + for (bit = range_first; bit <= range_last; bit++) > > > + mask |= (1 << bit); > > > > Are you sure that this would not overflow? > > > > I guess that it depends on how you have > > partitioned your CPUs. > > As far as I know there is no API stable and architecture portable way of > determining how many CPUs a machine has, which makes this whole > business a bit tricky. The problem starts with the partrt tool, which > should actually support arbitrarily long masks, but this hasn't > happened. There is sysconf(_SC_NPROCESSORS_CONF) which tries to collect this information from the system. It first tries /sys/devices/system/cpu/ then /proc/cpuinfo. > This is on the TODO list, so currently the test only guarantees 32 > CPUs. Should probably be documented somewhere... Right, at least add this into the documentation. -- Cyril Hrubis chrubis@suse.cz ------------------------------------------------------------------------------ Is your legacy SCM system holding you back? Join Perforce May 7 to find out: • 3 signs your SCM is hindering your productivity • Requirements for releasing software faster • Expert tips and advice for migrating your SCM now http://p.sf.net/sfu/perforce _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
* [LTP] [PATCH v3] partrt_nohz_full: Introducing a new test case 2014-04-28 15:06 ` [LTP] [PATCH v2] " Mats Liljegren 2014-04-28 15:06 ` [LTP] [PATCH] " Mats Liljegren @ 2014-05-13 14:11 ` Mats Liljegren 2014-05-13 14:11 ` Mats Liljegren 2014-05-28 16:45 ` Mats Liljegren 1 sibling, 2 replies; 28+ messages in thread From: Mats Liljegren @ 2014-05-13 14:11 UTC (permalink / raw) To: ltp-list; +Cc: Frederic Weisbecker, Kevin Hilman Changes from version 2: - Added "git submodule init" to README - Scrapped "verbose" command line parameter entirely, it wasn't implemented anyway - asprintf() replaced with fprintf() in launch_child() - Introduced cleanup function parameter to assert_exit_status() and shell(), making cleanup_entered variable in cleanup() obsolete - Documented 32 CPU limitation in README. The test code itself has been updated to handle all configured CPUs, but the partrt tool does not. - Simplified tool_available() by using tst_get_path() - Removed unnecessary exit() and return statements - Removed rand() to a simple "++" operation as work generator in the RT partition. Seemed like rand() could cause ticks for some reason. - Use SAFE_POPEN() instead of popen(). - Use a 2 second sleep before counting ticks, as a primitive way of emptying deferred work in the kernel. Changes from version 1: - Style fixes proposed by checkpatch.pl script - Have rt-tools as a git sub-module instead of requiring it to be cloned manually, builds can still be done without checking out rt-tools but you can't run partrt_nohz_full test successfully then - Check if cpuset supported by kernel, TCONF error if not - Check if rt-tools has been checked out before build, TCONF error if not - Simplified determined_nohz_mask() - Use parse_opts() instead of getopt_long() when parsing command line options - Moved partrt in SUBDIRS so that it is alphabetically correct in testcases/kernel/Makefile - launch_child will now handle moving child thread to the right CPUSET partition and CPU, which also eliminates the need for synchronization between parent and child - Children will no longer be able to cause any tst_* calls - Removed shell_silent(), above changes made it obsolete - Removed base parameter to str_to_ulong(), will always be 0 now - setup_children() is now called from main() rather than test() function. - Macros defining file names - Use C code for moving children to the right place instead of external scripts. Using script invocation from RT partition using NFS mounted file system will most likely invoke ticks which can make the test case fail - Added standard test loop in case reptitions are requested - Removed atomic operation for cleanup recursion detection, since it is only trying to protect recursive invocations from itself - Using timerfd to wait for timeout instead of busy-waiting - Removed pid_popen(), pid_wait() and shell_int() - assert_exit_status() now also checks if status is -1, i.e. command failed - Moved _GNU_SOURCE macro from Makefile to C file - Removed -Werror from Makefile - Removed err(), fail(), pass() and info() macros - Replaced usage of READ and WRITE macros with STDIN_FILENO and STDOUT_FILENO, READ and WRITE macros removed - Removed some unnecessary comments - setup() is renamed to setup_children() - Informational message are made mandatory, verbose only affects output from sub-commands, partrt in particular - Test case name is now partrt_nohz_full partrt_nohz_full is a test case which checks whether nohz_full mode can achieve 0Hz ticks (if relevant extra patches has been applied) or 1Hz ticks (mainline kernel). Appropriate kernel configurations and kernel boot parameters needed are documented in testcases/kernel/partrt/README. Mats Liljegren (1): partrt_nohz_full: Introducing a new test case .gitmodules | 3 + README.kernel_config | 6 + runtest/partrt_nohz_full | 1 + testcases/kernel/Makefile | 1 + testcases/kernel/partrt/Makefile | 17 + testcases/kernel/partrt/README | 66 +++ testcases/kernel/partrt/nohz_full/.gitignore | 1 + testcases/kernel/partrt/nohz_full/Makefile | 23 + .../partrt/nohz_full/test_partrt_nohz_full.c | 593 ++++++++++++++++++++ utils/.gitignore | 3 + utils/Makefile | 7 + utils/rt-tools | 1 + 12 files changed, 722 insertions(+) create mode 100644 runtest/partrt_nohz_full create mode 100644 testcases/kernel/partrt/Makefile create mode 100644 testcases/kernel/partrt/README create mode 100644 testcases/kernel/partrt/nohz_full/.gitignore create mode 100644 testcases/kernel/partrt/nohz_full/Makefile create mode 100644 testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c create mode 160000 utils/rt-tools -- 1.7.10.4 ------------------------------------------------------------------------------ "Accelerate Dev Cycles with Automated Cross-Browser Testing - For FREE Instantly run your Selenium tests across 300+ browser/OS combos. Get unparalleled scalability from the best Selenium testing platform available Simple to use. Nothing to install. Get started now for free." http://p.sf.net/sfu/SauceLabs _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
* [LTP] [PATCH v3] partrt_nohz_full: Introducing a new test case 2014-05-13 14:11 ` [LTP] [PATCH v3] " Mats Liljegren @ 2014-05-13 14:11 ` Mats Liljegren 2014-06-02 17:17 ` chrubis 2014-05-28 16:45 ` Mats Liljegren 1 sibling, 1 reply; 28+ messages in thread From: Mats Liljegren @ 2014-05-13 14:11 UTC (permalink / raw) To: ltp-list; +Cc: Frederic Weisbecker, Kevin Hilman This test case is intended to test the nohz_full kernel feature. See file testcases/kernel/partrt/README for more information. Signed-off-by: Mats Liljegren <mats.liljegren@enea.com> --- .gitmodules | 3 + README.kernel_config | 6 + runtest/partrt_nohz_full | 1 + testcases/kernel/Makefile | 1 + testcases/kernel/partrt/Makefile | 17 + testcases/kernel/partrt/README | 66 +++ testcases/kernel/partrt/nohz_full/.gitignore | 1 + testcases/kernel/partrt/nohz_full/Makefile | 23 + .../partrt/nohz_full/test_partrt_nohz_full.c | 593 ++++++++++++++++++++ utils/.gitignore | 3 + utils/Makefile | 7 + utils/rt-tools | 1 + 12 files changed, 722 insertions(+) create mode 100644 runtest/partrt_nohz_full create mode 100644 testcases/kernel/partrt/Makefile create mode 100644 testcases/kernel/partrt/README create mode 100644 testcases/kernel/partrt/nohz_full/.gitignore create mode 100644 testcases/kernel/partrt/nohz_full/Makefile create mode 100644 testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c create mode 160000 utils/rt-tools diff --git a/.gitmodules b/.gitmodules index 1c9e9c3..0461cbd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "testcases/kernel/mce-test"] path = testcases/kernel/mce-test url = git://git.kernel.org/pub/scm/linux/kernel/git/gong.chen/mce-test.git +[submodule "utils/rt-tools"] + path = utils/rt-tools + url = https://github.com/OpenEneaLinux/rt-tools diff --git a/README.kernel_config b/README.kernel_config index 54df0ad..5ebfe7e 100644 --- a/README.kernel_config +++ b/README.kernel_config @@ -311,3 +311,9 @@ And the version of packages must be 1.41.4 or above. For more information to build/install/run these tests, look through: ltp/testcases/kernel/fs/ext4-new-features/README --------------------------------- + +--------------------------------- +Enabling Kernel Configuration to run test partrt_nohz_full +--------------------------------- +CONFIG_NO_HZ_FULL=y +CONFIG_CPUSETS=y diff --git a/runtest/partrt_nohz_full b/runtest/partrt_nohz_full new file mode 100644 index 0000000..d84e192 --- /dev/null +++ b/runtest/partrt_nohz_full @@ -0,0 +1 @@ +partrt_nohz_full test_partrt_nohz_full -d 3000 diff --git a/testcases/kernel/Makefile b/testcases/kernel/Makefile index 6bffe79..cdd0406 100644 --- a/testcases/kernel/Makefile +++ b/testcases/kernel/Makefile @@ -47,6 +47,7 @@ SUBDIRS += connectors \ logging \ mem \ numa \ + partrt \ performance_counters \ pty \ sched \ diff --git a/testcases/kernel/partrt/Makefile b/testcases/kernel/partrt/Makefile new file mode 100644 index 0000000..120090e --- /dev/null +++ b/testcases/kernel/partrt/Makefile @@ -0,0 +1,17 @@ +# Copyright (C) 2014, Enea AB. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# + +top_srcdir ?= ../../.. + +include $(top_srcdir)/include/mk/env_pre.mk +include $(top_srcdir)/include/mk/generic_trunk_target.mk diff --git a/testcases/kernel/partrt/README b/testcases/kernel/partrt/README new file mode 100644 index 0000000..a461b25 --- /dev/null +++ b/testcases/kernel/partrt/README @@ -0,0 +1,66 @@ +partrt_nohz_full +================ + +This directory contains part_nohz_full test case for verifying absence of ticks +on nohz_full enabled CPUs. For more information about nohz_full and how to +enable this feature, see the following document in the kernel source code: + + Documentation/timers/NO_HZ.txt + +CPU partitioning is a way of isolating work with real-time requirements from +work without real-time requirements by assuring that the real-time work is done +on dedicated CPUs. This uses the kernel feature cgroup/cpuset. The goal is to +make sure that nohz_full mode can be enabled on these CPUs. + +Dependencies +------------ +CPU partitioning is performed by the partrt tool, which is available as a git +submodule. Before building, do the following from the LTP root directory to +include rt-tools in the build: + + git submodule init # If you haven't done this since cloning + git submodule update utils/rt-tools + +The following build time kernel configurations must be enabled: + + CONFIG_NO_HZ_FULL=y + CONFIG_CPUSETS=y + +These kernel configuration parameters are also documented in the +READ.kernel_config file in LTP root directory. + +The following kernel boot parameter needs to be set: + + nohz_full=<cpu list> + +There might be other parameters needed to actually get nohz_full working, but +this is largely dependent on architecture and kernel version. + +How to run +---------- + +The partrt_nohz_full test is run by runltp like so: + + ./runltp -f partrt_nohz_full + +To make life interesting, you can play with "-n" and "-i" options to runltp, to +generate some load in the non-realtime partition: + + ./runltp -n -i 5 -f partrt_nohz_full + +To get some more help with the cause of a failing test, do this as root before +running the test: + + echo 1 > /sys/kernel/debug/tracing/events/enable + +and then look at trace after test failed: + + cat /sys/kernel/debug/tracing/trace + +Since "count_ticks" also uses ftrace and will update tracing_cpumask to only +trace the nohz_full CPUs, the trace should be close to empty if the run was +successful. + +Known limitations +----------------- +Currently this test only supports machines with up to 32 CPUs. diff --git a/testcases/kernel/partrt/nohz_full/.gitignore b/testcases/kernel/partrt/nohz_full/.gitignore new file mode 100644 index 0000000..5848b21 --- /dev/null +++ b/testcases/kernel/partrt/nohz_full/.gitignore @@ -0,0 +1 @@ +/test_partrt_nohz_full diff --git a/testcases/kernel/partrt/nohz_full/Makefile b/testcases/kernel/partrt/nohz_full/Makefile new file mode 100644 index 0000000..5528dc0 --- /dev/null +++ b/testcases/kernel/partrt/nohz_full/Makefile @@ -0,0 +1,23 @@ +# +# Copyright (C) 2014, Enea AB. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +# the GNU General Public License for more details. +# + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk + +LDLIBS += -lrt + +MAKE_TARGETS := test_partrt_nohz_full + +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c b/testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c new file mode 100644 index 0000000..a5ffe3b --- /dev/null +++ b/testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c @@ -0,0 +1,593 @@ +/* + * Copyright (C) 2014, Enea AB. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + */ + +#define _GNU_SOURCE + +#include <errno.h> +#include <string.h> +#include <getopt.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/syscall.h> +#include <sys/stat.h> +#include <sys/timerfd.h> +#include <sys/sysinfo.h> +#include <stdarg.h> +#include <inttypes.h> +#include <limits.h> +#include <time.h> +#include <sched.h> +#include <fcntl.h> + +#include <test.h> +#include <usctest.h> +#include <safe_macros.h> +#include <safe_stdio.h> + +const char *TCID = "partrt_nohz_full"; +const int TST_TOTAL = 1; + +/* Used for RT load */ +volatile int dummy_value; + +/* When true (1), all children will terminate */ +static volatile int time_is_up; + +/* Name of application as given in argv[0] */ +static const char *appname; + +/* Boolean describing whether RT partition in cpuset needs to be created */ +static int need_partrt_create = 0; + +/* RT load function prototype */ +typedef void (child_func)(void); + +/* Amount of stack for RT load threads */ +#define STACK_SZ (64 * 1024) + +/* Default path to cgroups/cpuset */ +#define CPUSET_ROOT "/sys/fs/cgroup/cpuset" + +/* Expected path to cpuset RT partition */ +#define CPUSET_RT_PATH CPUSET_ROOT "/rt" + +/* Expected path to RT partition tasks list file */ +#define CPUSET_RT_TASKS_FILE CPUSET_RT_PATH "/tasks" + +/* Expected path to RT partition CPU list file */ +#define CPUSET_RT_CPUS_FILE CPUSET_RT_PATH "/cpuset.cpus" + +/* File to look for to determine whether true 0Hz can be achieved */ +#define SCHED_TICK_MAX_FILE "/sys/kernel/debug/sched_tick_max_deferment" + +/* File containing a list of filesystems supported by the kernel */ +#define PROC_FILESYSTEMS_FILE "/proc/filesystems" + +static void cleanup(void); + +/* + * CPU mask handling functions. + */ + +static int cpuset_nr_cpus(void) +{ + static int nr_cpus; + + if (nr_cpus == 0) { + nr_cpus = get_nprocs_conf(); + if (nr_cpus <= 0) + tst_brkm(TCONF, cleanup, + "Could not determine number of CPUs"); + tst_resm(TINFO, "%d CPUs configured in kernel", nr_cpus); + } + + return nr_cpus; +} + +static cpu_set_t *cpuset_alloc_zero(void) +{ + cpu_set_t *set; + + set = CPU_ALLOC(cpuset_nr_cpus()); + if (set == NULL) + tst_brkm(TBROK, cleanup, + "Out of memory allocating CPU mask for %d CPUs", + cpuset_nr_cpus()); + + CPU_ZERO_S(CPU_ALLOC_SIZE(cpuset_nr_cpus()), set); + + return set; +} + +static void cpuset_free(cpu_set_t *set) +{ + CPU_FREE(set); +} + +static void cpuset_set(int cpu, cpu_set_t *set) +{ + if (cpu >= cpuset_nr_cpus()) + tst_brkm(TBROK, cleanup, "Internal error: Illegal use of cpuset_set(%d,...): Only %d CPUs in the set", + cpu, cpuset_nr_cpus()); + CPU_SET_S(cpu, CPU_ALLOC_SIZE(cpuset_nr_cpus()), set); +} + +static cpu_set_t *cpuset_alloc_set(int cpu) +{ + cpu_set_t *set = cpuset_alloc_zero(); + + cpuset_set(cpu, set); + + return set; +} + +static int cpuset_isset(int cpu, const cpu_set_t *set) +{ + if (cpu > cpuset_nr_cpus()) + return 0; + return CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(cpuset_nr_cpus()), set); +} + +static size_t cpuset_alloc_size(void) +{ + return CPU_ALLOC_SIZE(cpuset_nr_cpus()); +} + +static const char *cpuset_hex(const cpu_set_t *set) +{ + static char *str; + char *curr; + int cpu; + const int nr_cpus = cpuset_nr_cpus(); + const size_t str_size = 1 /* NUL */ + (nr_cpus / 4); + + if (str == NULL) + str = SAFE_MALLOC(cleanup, str_size); + + curr = str + str_size - 1; + *curr = '\0'; + + while (cpu < nr_cpus) { + char ch = 0; + int bit; + for (bit = 0; bit < 4; bit++, cpu++) { + if (cpuset_isset(cpu, set)) + ch |= (1 << bit); + } + curr--; + if (ch > 9) + *curr = 'a' + (ch - 10); + else + *curr = '0' + ch; + } + + return str; +} + +/* + * Check exit status value. + * If any signs of error is found, report error and abort testing. + */ +static void assert_exit_status(void (*cleanup_fn) (void), const char *cmd, + int status) +{ + if (status == -1) + tst_brkm(TBROK | TERRNO, cleanup_fn, + "%s: Could not execute command", cmd); + if (WIFSIGNALED(status)) { + tst_brkm(TBROK, cleanup_fn, + "%s: Child terminated unexpectedly due to signal nr %d", + cmd, + WTERMSIG(status)); + } + if (!WIFEXITED(status)) { + tst_brkm(TBROK, cleanup_fn, "%s: Child terminated unexpectedly", + cmd); + } + if (WEXITSTATUS(status) != 0) { + tst_brkm(TBROK, cleanup_fn, + "%s: Child exited with exit status %d", + cmd, + WEXITSTATUS(status)); + } +} + +/* + * Execute given command using shell, ensure success (0) is returned. + */ +static void shell(void (*cleanup_fn) (void), const char *cmd, ...) +{ + char *cmd_buf; + va_list va; + + va_start(va, cmd); + if (vasprintf(&cmd_buf, cmd, va) == -1) + tst_brkm(TBROK, cleanup_fn, + "%s: Not valid printf format, or out of memory", cmd); + va_end(va); + + tst_resm(TINFO, "%s: Executing", cmd_buf); + assert_exit_status(cleanup_fn, cmd_buf, system(cmd_buf)); + tst_resm(TINFO, "%s: Returns success", cmd_buf); + + free(cmd_buf); +} + +/* + * Convert string to unsigned long. + * Error prefix expressed as string. + */ +static unsigned long str_to_ulong(const char *str, const char *err_prefix) +{ + char *endptr; + unsigned long val; + val = strtoull(str, &endptr, 0); + if (endptr == str) + tst_brkm(TBROK, cleanup, + "%s: %s: Expected unsigned decimal value", + err_prefix, str); + if (*endptr != '\0') + tst_brkm(TBROK, cleanup, + "%s: %s: Garbage character '%c' found after decimal value", + err_prefix, str, *endptr); + + return val; +} + +/* + * Call a command using shell. + * Command line expressed as va_list. + * Returns the command's first line of output. + */ +static char *shell_str(char *dest, int size, const char *cmd, ...) +{ + int len; + char *cmd_buf; + FILE *file; + va_list va; + + /* Build command line */ + va_start(va, cmd); + if (vasprintf(&cmd_buf, cmd, va) == -1) + tst_brkm(TBROK, cleanup, + "%s: Not valid printf format, or out of memory", cmd); + va_end(va); + + /* Launch command */ + file = SAFE_POPEN(cleanup, cmd_buf, "r"); + + tst_resm(TINFO, "%s: Executing", cmd_buf); + + /* Read commands stdout */ + if (fgets(dest, size, file) == NULL) { + if (feof(file)) + tst_brkm(TBROK, cleanup, + "%s: Expected output from the command, but got nothing", + cmd_buf); + else + tst_brkm(TBROK | TERRNO, cleanup, + "%s: Could not read command output", + cmd_buf); + } + + /* Get rid of terminating newline, if any */ + len = strlen(dest); + if (dest[len-1] == '\n') + dest[len-1] = '\0'; + + /* Wait until command execution finish. + * Main reason for doing this even though we've got what we want is for + * better error detection. The alternative could be to let cleanup() + * handle it if process hasn't finished by then. */ + assert_exit_status(cleanup, cmd_buf, pclose(file)); + + tst_resm(TINFO, "%s: Returns: %s", cmd_buf, dest); + + free(cmd_buf); + + return dest; +} + +static int child_entry(void *arg) +{ + (void) arg; + + while (!time_is_up) + dummy_value++; + + return 0; +} + +/* + * Start a new thread executing the given function, on the given cpuset + * partition and CPU ID. Note that the CPU ID must be one of the + * available CPUs in the cpuset partition. + */ +static void launch_child(int cpu) +{ + char *stack = SAFE_MALLOC(cleanup, STACK_SZ); + int tid; + cpu_set_t * const cpu_set = cpuset_alloc_set(cpu); + + tid = ltp_clone(CLONE_VM, child_entry, NULL, STACK_SZ, stack); + if (tid == -1) + tst_brkm(TBROK | TERRNO, cleanup, "clone() failed"); + + /* Move child to RT partition */ + SAFE_FILE_PRINTF(cleanup, CPUSET_RT_TASKS_FILE, "%ld", (long) tid); + + /* Move child to indicated CPU within the RT partition */ + if (sched_setaffinity(tid, cpuset_alloc_size(), cpu_set) < 0) + tst_brkm(TBROK | TERRNO, cleanup, + "pid %u: sched_setaffinity() failed", + tid); + + tst_resm(TINFO, "pid %d: Starting RT load on cpu %d", + tid, cpu); + + cpuset_free(cpu_set); +} + +static void cleanup(void) +{ + pid_t pid; + int status; + + tst_resm(TINFO, "Cleanup: Terminating children"); + + time_is_up = 1; + + do { + pid = wait(&status); + if (pid != -1) { + char cmd[64]; + snprintf(cmd, sizeof(cmd), "Pid %lu", + (unsigned long) pid); + assert_exit_status(NULL, cmd, status); + tst_resm(TINFO, + "Cleanup: %s: Has terminated successfully", + cmd); + } + } while (pid != -1); + + if (errno != ECHILD) + tst_brkm(TBROK | TERRNO, NULL, "Cleanup: wait() failed"); + + if (need_partrt_create) + shell(NULL, "partrt undo"); + + tst_resm(TINFO, "Cleanup: Done"); +} + +static cpu_set_t *alloc_nohz_mask(void) +{ + int range_first; + int range_last; + int bit; + cpu_set_t *mask = cpuset_alloc_zero(); + FILE *stream = SAFE_FOPEN(cleanup, CPUSET_RT_CPUS_FILE, "r"); + int nr_matches; + + for (nr_matches = fscanf(stream, "%d-%d", &range_first, &range_last); + nr_matches > 0; + nr_matches = fscanf(stream, ",%d-%d", &range_first, &range_last)) { + if (nr_matches == 1) + range_last = range_first; + + /* Set all bits in range */ + for (bit = range_first; bit <= range_last; bit++) + cpuset_set(bit, mask); + } + + SAFE_FCLOSE(cleanup, stream); + + if (nr_matches == -1) + tst_brkm(TBROK | TERRNO, cleanup, "%s: fscanf() failed", + CPUSET_RT_CPUS_FILE); + + return mask; +} + +static void tool_available(char *tool_name) +{ + char full_path[PATH_MAX]; + + if (tst_get_path(tool_name, full_path, sizeof(full_path)) != 0) + tst_brkm(TCONF, cleanup, + "%s tool not found, skipping test. Use 'git submodule update utils/rt-tools' to include needed tools in the build.", + tool_name); + + if (access(full_path, X_OK) != 0) + tst_brkm(TCONF, cleanup, + "%s tool found in PATH, but is not executable.", + tool_name); +} + +static void tools_check(void) +{ + tool_available("partrt"); + tool_available("list2mask"); + tool_available("count_ticks"); +} + +static void cpuset_check(void) +{ + FILE * const stream = SAFE_FOPEN(cleanup, PROC_FILESYSTEMS_FILE, "r"); + char *name; + + while (fscanf(stream, "%as", &name) > 0) { + if (strcmp(name, "cpuset") == 0) { + free(name); + return; + } + free(name); + } + + tst_brkm(TCONF, cleanup, "CPUSET not configured in kernel"); +} + +/* + * Setup and return number of child threads + */ +static int setup_children(void) +{ + cpu_set_t *nohz_mask; + int bit; + int nr_children = 0; + + time_is_up = 0; + + tools_check(); + tst_require_root(NULL); + cpuset_check(); + + if (access(CPUSET_RT_PATH, F_OK) == -1) + need_partrt_create = 1; + + if (need_partrt_create) + shell(cleanup, "partrt create $(list2mask --nohz)"); + + nohz_mask = alloc_nohz_mask(); + tst_resm(TINFO, "Nohz CPU mask: 0x%s", cpuset_hex(nohz_mask)); + + for (bit = 0; bit < (int) (sizeof(nohz_mask) * 8); bit++) { + if (cpuset_isset(bit, nohz_mask)) { + launch_child(bit); + nr_children++; + } + } + + tst_resm(TINFO, "All children started"); + + cpuset_free(nohz_mask); + + return nr_children; +} + +/* + * Perform tick measurement for the given number of seconds. + */ +static void test(time_t duration, unsigned nr_children) +{ + uint64_t nr_ticks; + time_t time_finished; + /* If sched_tick_max_deferment patch has been applied, expect that the + * partitioning has disabled ticks completely. Otherwise, expect + * 1Hz ticks */ + const int expect_0hz = + access(SCHED_TICK_MAX_FILE, F_OK) == 0; + const time_t current_time = time(NULL); + + const uint64_t expected_ticks = expect_0hz ? 0 : duration * nr_children; + char val_str[64]; + static const char count_ticks_end_cmd[] = + "count_ticks --cpu rt --batch --end"; + int timer_fd; + struct itimerspec timeout = { {0}, {0} }; + uint64_t nr_timeouts; + + /* Make sure process migration has completed before looking for ticks. + * Sleep is somewhat primitive, but should work. */ + sleep(2); + + shell(cleanup, "count_ticks --cpu rt --start"); + + time_finished = time(NULL) + duration; + + tst_resm(TINFO, "Execution started: %s", ctime(¤t_time)); + tst_resm(TINFO, "Execution ends : %s", ctime(&time_finished)); + + timer_fd = timerfd_create(CLOCK_MONOTONIC, 0); + timeout.it_value.tv_sec = duration; + timerfd_settime(timer_fd, 0, &timeout, NULL); + + /* Wait for timeout */ + SAFE_READ(cleanup, 1, timer_fd, &nr_timeouts, sizeof(nr_timeouts)); + if (nr_timeouts != 1) + tst_brkm(TBROK, cleanup, + "Multiple timeouts when single timeout was requested"); + + shell_str(val_str, sizeof(val_str), count_ticks_end_cmd); + nr_ticks = str_to_ulong(val_str, count_ticks_end_cmd); + + if (expect_0hz) { + if (nr_ticks != 0) + tst_resm(TFAIL, "Expected no ticks, but got %" PRIu64, + nr_ticks); + else + tst_resm(TPASS, "No ticks occurred"); + } else { + if (nr_ticks > expected_ticks) + tst_resm(TFAIL, + "Expected maximum %" PRIu64 + " ticks, but got %" PRIu64, + expected_ticks, nr_ticks); + else + tst_resm(TPASS, + "%" PRIu64 + " ticks occurred, which was expected", + nr_ticks); + } +} + +static void usage(void) +{ + printf( + "Usage: %s [options]\n" + " %s --help\n" + "Test whether a CPU can be isolated and made tickless even under load.\n" + "\n" + " -h Display this usage text and exit.\n" + " -d <secs> Number of seconds to run the test.\n" + , + appname, appname + ); +} + +int main(int argc, char *argv[]) +{ + long duration = 0; + int lc; + unsigned int nr_children; + char *error_msg; + char *duration_str = NULL; + + const option_t options[] = { + {"d:", NULL, &duration_str}, + {NULL, NULL, NULL} + }; + + appname = basename(argv[0]); + + error_msg = parse_opts(argc, argv, options, usage); + if (error_msg != NULL) + tst_brkm(TBROK, NULL, "Error parsing command line: %s", + error_msg); + + if (duration_str == NULL) + tst_brkm(TBROK, cleanup, + "No duration specified, nothing to do"); + + duration = (long) str_to_ulong(duration_str, "-d"); + + tst_resm(TINFO, "%s: Compiled %s %s", __FILE__, __DATE__, __TIME__); + + nr_children = setup_children(); + + for (lc = 0; TEST_LOOPING(lc); lc++) + test(duration, nr_children); + + cleanup(); + tst_exit(); +} diff --git a/utils/.gitignore b/utils/.gitignore index a582748..f63e51a 100644 --- a/utils/.gitignore +++ b/utils/.gitignore @@ -48,3 +48,6 @@ /sctp/func_tests/test_tcp_style_v6 /sctp/func_tests/test_timetolive /sctp/func_tests/test_timetolive_v6 +/count_ticks +/list2mask +/partrt diff --git a/utils/Makefile b/utils/Makefile index 1508b35..c3df6cd 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -24,6 +24,10 @@ include $(top_srcdir)/include/mk/env_pre.mk MAKE_TARGETS += ffsb +ifneq ($(wildcard rt-tools),) +MAKE_TARGETS += partrt list2mask count_ticks +endif + FFSBDIR := ffsb-6.0-rc2 FILTER_OUT_DIRS := $(FFSBDIR) FFSB := $(FFSBDIR)/ffsb @@ -35,6 +39,9 @@ $(FFSB): $(abs_srcdir)/$(FFSBDIR) ffsb: $(FFSB) cp $(FFSB) ffsb +partrt list2mask count_ticks: + cp rt-tools/install/bin/$@ $@ + trunk-all: $(FFSB) trunk-clean:: | ffsb-clean diff --git a/utils/rt-tools b/utils/rt-tools new file mode 160000 index 0000000..f75b334 --- /dev/null +++ b/utils/rt-tools @@ -0,0 +1 @@ +Subproject commit f75b334922a2243d0b757c0627c6f1c8440818c0 -- 1.7.10.4 ------------------------------------------------------------------------------ "Accelerate Dev Cycles with Automated Cross-Browser Testing - For FREE Instantly run your Selenium tests across 300+ browser/OS combos. Get unparalleled scalability from the best Selenium testing platform available Simple to use. Nothing to install. Get started now for free." http://p.sf.net/sfu/SauceLabs _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [LTP] [PATCH v3] partrt_nohz_full: Introducing a new test case 2014-05-13 14:11 ` Mats Liljegren @ 2014-06-02 17:17 ` chrubis [not found] ` <20140603104018.3b0cba6f@mats-desktop> 0 siblings, 1 reply; 28+ messages in thread From: chrubis @ 2014-06-02 17:17 UTC (permalink / raw) To: Mats Liljegren; +Cc: ltp-list, Kevin Hilman, Frederic Weisbecker Hi! > This test case is intended to test the nohz_full kernel feature. See file > testcases/kernel/partrt/README for more information. This version looks good to me. My last concern is about the names of the partrt tools. > --- a/utils/Makefile > +++ b/utils/Makefile > @@ -24,6 +24,10 @@ include $(top_srcdir)/include/mk/env_pre.mk > > MAKE_TARGETS += ffsb > > +ifneq ($(wildcard rt-tools),) > +MAKE_TARGETS += partrt list2mask count_ticks > +endif > + > FFSBDIR := ffsb-6.0-rc2 > FILTER_OUT_DIRS := $(FFSBDIR) > FFSB := $(FFSBDIR)/ffsb > @@ -35,6 +39,9 @@ $(FFSB): $(abs_srcdir)/$(FFSBDIR) > ffsb: $(FFSB) > cp $(FFSB) ffsb > > +partrt list2mask count_ticks: > + cp rt-tools/install/bin/$@ $@ As it is now, upon the installation LTP copies all executables into one directory and we are trying to avoid collisions by adding sufixes or prefixes to the binaries. Can we add a rt to all the binaries (list2maskrt, count_ticksrt) or something similar while we install them? -- Cyril Hrubis chrubis@suse.cz ------------------------------------------------------------------------------ Learn Graph Databases - Download FREE O'Reilly Book "Graph Databases" is the definitive new guide to graph databases and their applications. Written by three acclaimed leaders in the field, this first edition is now available. Download your free book today! http://p.sf.net/sfu/NeoTech _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
[parent not found: <20140603104018.3b0cba6f@mats-desktop>]
* Re: [LTP] [PATCH v3] partrt_nohz_full: Introducing a new test case [not found] ` <20140603104018.3b0cba6f@mats-desktop> @ 2014-06-03 11:31 ` chrubis [not found] ` <20141030171737.3eb800f1@mats-desktop> 1 sibling, 0 replies; 28+ messages in thread From: chrubis @ 2014-06-03 11:31 UTC (permalink / raw) To: Mats Liljegren; +Cc: ltp-list, Kevin Hilman, Frederic Weisbecker Hi! > I could change partrt to accept CPU list directly instead of being > dependent on list2mask tool, which means that list2mask disappears. > > As for count_ticks, a better name might be kernel_ticks. > count_kernel_ticks would be possible but is a bit too long for me... > > Would having the tool names > > partrt > kernel_ticks > > be good enough for ltp, or do you want kernel_ticks prefixed? If so, > I'd propose: > > partrt > partrt_kernel_ticks > > In this case the original name kernel_ticks will be renamed for ltp > only. > > What do you prefer? Prefixing with partrt_ for LTP (upon installation) sounds good to me. -- Cyril Hrubis chrubis@suse.cz ------------------------------------------------------------------------------ Learn Graph Databases - Download FREE O'Reilly Book "Graph Databases" is the definitive new guide to graph databases and their applications. Written by three acclaimed leaders in the field, this first edition is now available. Download your free book today! http://p.sf.net/sfu/NeoTech _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
[parent not found: <20141030171737.3eb800f1@mats-desktop>]
* Re: [LTP] [PATCH v3] partrt_nohz_full: Introducing a new test case [not found] ` <20141030171737.3eb800f1@mats-desktop> @ 2014-11-26 13:40 ` Cyril Hrubis 0 siblings, 0 replies; 28+ messages in thread From: Cyril Hrubis @ 2014-11-26 13:40 UTC (permalink / raw) To: Mats Liljegren; +Cc: ltp-list, Kevin Hilman, Frederic Weisbecker Hi! > The new version of partrt has a tool named "bitcalc", which performs > those nasty bit calculations in a C application instead of an ugly Bash > script. This made the partrt dash compatible, and also got rid of > list2mask script. > > So the new tool names are: > - partrt > - bitcalc > - count_ticks > > Having a prefix on bitcalc presents a problem since partrt is using it. > This would mean that I have to inform the script that the expected tool > is named something else, which I think will make this script a bit ugly. > > Prefixing partrt and count_ticks should not be a problem though. > Assuming we use partrt as prefix (probably safer than just rt), it > would become: > > - partrt_partrt > - bitcalc > - partrt_count_ticks Sounds reasonable to me. -- Cyril Hrubis chrubis@suse.cz ------------------------------------------------------------------------------ Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server from Actuate! Instantly Supercharge Your Business Reports and Dashboards with Interactivity, Sharing, Native Excel Exports, App Integration & more Get technology previously reserved for billion-dollar corporations, FREE http://pubads.g.doubleclick.net/gampad/clk?id=157005751&iu=/4140/ostg.clktrk _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [LTP] [PATCH v3] partrt_nohz_full: Introducing a new test case 2014-05-13 14:11 ` [LTP] [PATCH v3] " Mats Liljegren 2014-05-13 14:11 ` Mats Liljegren @ 2014-05-28 16:45 ` Mats Liljegren 2014-05-29 12:21 ` chrubis 1 sibling, 1 reply; 28+ messages in thread From: Mats Liljegren @ 2014-05-28 16:45 UTC (permalink / raw) To: Mats Liljegren; +Cc: ltp-list, Kevin Hilman, Frederic Weisbecker Ping? Regards Mats Liljegren On Tue, 13 May 2014 16:11:08 +0200 Mats Liljegren <mats.liljegren@enea.com> wrote: > Changes from version 2: > - Added "git submodule init" to README > - Scrapped "verbose" command line parameter entirely, it wasn't > implemented anyway > - asprintf() replaced with fprintf() in launch_child() > - Introduced cleanup function parameter to assert_exit_status() and > shell(), making cleanup_entered variable in cleanup() obsolete > - Documented 32 CPU limitation in README. The test code itself has > been updated to handle all configured CPUs, but the partrt tool does > not. > - Simplified tool_available() by using tst_get_path() > - Removed unnecessary exit() and return statements > - Removed rand() to a simple "++" operation as work generator in the > RT partition. Seemed like rand() could cause ticks for some reason. > - Use SAFE_POPEN() instead of popen(). > - Use a 2 second sleep before counting ticks, as a primitive way of > emptying deferred work in the kernel. > > Changes from version 1: > - Style fixes proposed by checkpatch.pl script > - Have rt-tools as a git sub-module instead of requiring it to be > cloned manually, builds can still be done without checking out > rt-tools but you can't run partrt_nohz_full test successfully then > - Check if cpuset supported by kernel, TCONF error if not > - Check if rt-tools has been checked out before build, TCONF error if > not > - Simplified determined_nohz_mask() > - Use parse_opts() instead of getopt_long() when parsing command line > options > - Moved partrt in SUBDIRS so that it is alphabetically correct in > testcases/kernel/Makefile > - launch_child will now handle moving child thread to the right > CPUSET partition and CPU, which also eliminates the need for > synchronization between parent and child > - Children will no longer be able to cause any tst_* calls > - Removed shell_silent(), above changes made it obsolete > - Removed base parameter to str_to_ulong(), will always be 0 now > - setup_children() is now called from main() rather than test() > function. > - Macros defining file names > - Use C code for moving children to the right place instead of > external scripts. Using script invocation from RT partition using NFS > mounted file system will most likely invoke ticks which can make the > test case fail > - Added standard test loop in case reptitions are requested > - Removed atomic operation for cleanup recursion detection, since it > is only trying to protect recursive invocations from itself > - Using timerfd to wait for timeout instead of busy-waiting > - Removed pid_popen(), pid_wait() and shell_int() > - assert_exit_status() now also checks if status is -1, i.e. command > failed > - Moved _GNU_SOURCE macro from Makefile to C file > - Removed -Werror from Makefile > - Removed err(), fail(), pass() and info() macros > - Replaced usage of READ and WRITE macros with STDIN_FILENO and > STDOUT_FILENO, READ and WRITE macros removed > - Removed some unnecessary comments > - setup() is renamed to setup_children() > - Informational message are made mandatory, verbose only affects > output from sub-commands, partrt in particular > - Test case name is now partrt_nohz_full > > partrt_nohz_full is a test case which checks whether nohz_full mode > can achieve 0Hz ticks (if relevant extra patches has been applied) or > 1Hz ticks (mainline kernel). > > Appropriate kernel configurations and kernel boot parameters needed > are documented in testcases/kernel/partrt/README. > > Mats Liljegren (1): > partrt_nohz_full: Introducing a new test case > > .gitmodules | 3 + > README.kernel_config | 6 + > runtest/partrt_nohz_full | 1 + > testcases/kernel/Makefile | 1 + > testcases/kernel/partrt/Makefile | 17 + > testcases/kernel/partrt/README | 66 +++ > testcases/kernel/partrt/nohz_full/.gitignore | 1 + > testcases/kernel/partrt/nohz_full/Makefile | 23 + > .../partrt/nohz_full/test_partrt_nohz_full.c | 593 > ++++++++++++++++++++ > utils/.gitignore | 3 + > utils/Makefile | 7 + > utils/rt-tools | 1 + 12 files > changed, 722 insertions(+) create mode 100644 runtest/partrt_nohz_full > create mode 100644 testcases/kernel/partrt/Makefile > create mode 100644 testcases/kernel/partrt/README > create mode 100644 testcases/kernel/partrt/nohz_full/.gitignore > create mode 100644 testcases/kernel/partrt/nohz_full/Makefile > create mode 100644 > testcases/kernel/partrt/nohz_full/test_partrt_nohz_full.c create mode > 160000 utils/rt-tools > ------------------------------------------------------------------------------ Time is money. Stop wasting it! Get your web API in 5 minutes. www.restlet.com/download http://p.sf.net/sfu/restlet _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [LTP] [PATCH v3] partrt_nohz_full: Introducing a new test case 2014-05-28 16:45 ` Mats Liljegren @ 2014-05-29 12:21 ` chrubis 0 siblings, 0 replies; 28+ messages in thread From: chrubis @ 2014-05-29 12:21 UTC (permalink / raw) To: Mats Liljegren; +Cc: ltp-list, Kevin Hilman, Frederic Weisbecker Hi! > Ping? Sorry it takes that long, you are in the queue, I should get to the patch either today, the worstcase should be on Monday. -- Cyril Hrubis chrubis@suse.cz ------------------------------------------------------------------------------ Time is money. Stop wasting it! Get your web API in 5 minutes. www.restlet.com/download http://p.sf.net/sfu/restlet _______________________________________________ Ltp-list mailing list Ltp-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ltp-list ^ permalink raw reply [flat|nested] 28+ messages in thread
end of thread, other threads:[~2014-11-26 13:41 UTC | newest]
Thread overview: 28+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-03-10 13:50 [LTP] Testing absence of ticks with nohz_full Mats Liljegren
2014-03-10 14:04 ` chrubis
2014-03-10 15:17 ` Frederic Weisbecker
2014-03-10 15:27 ` Steven Rostedt
2014-03-10 15:33 ` Frederic Weisbecker
2014-03-11 10:34 ` Mats Liljegren
2014-03-11 23:31 ` Steven Rostedt
2014-03-13 22:10 ` Kevin Hilman
2014-03-17 16:35 ` Mats Liljegren
2014-04-16 15:48 ` [LTP] [RFC][PATCH] partrt_nohz_full: Introducing a new test case Mats Liljegren
2014-04-16 15:48 ` Mats Liljegren
2014-04-22 15:47 ` chrubis
[not found] ` <20140423124410.29874232@mats-desktop>
2014-04-23 11:34 ` chrubis
[not found] ` <20140424105218.5cd2b5bf@mats-desktop>
2014-04-24 9:06 ` chrubis
[not found] ` <20140424140358.63dac752@mats-desktop>
2014-04-24 12:35 ` chrubis
2014-04-22 14:07 ` chrubis
[not found] ` <20140423084101.536f03f0@mats-desktop>
2014-04-23 10:24 ` chrubis
2014-04-28 15:06 ` [LTP] [PATCH v2] " Mats Liljegren
2014-04-28 15:06 ` [LTP] [PATCH] " Mats Liljegren
2014-05-06 16:20 ` chrubis
[not found] ` <20140507132016.40361a0a@mats-desktop>
2014-05-07 12:17 ` chrubis
2014-05-13 14:11 ` [LTP] [PATCH v3] " Mats Liljegren
2014-05-13 14:11 ` Mats Liljegren
2014-06-02 17:17 ` chrubis
[not found] ` <20140603104018.3b0cba6f@mats-desktop>
2014-06-03 11:31 ` chrubis
[not found] ` <20141030171737.3eb800f1@mats-desktop>
2014-11-26 13:40 ` Cyril Hrubis
2014-05-28 16:45 ` Mats Liljegren
2014-05-29 12:21 ` chrubis
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox