All of lore.kernel.org
 help / color / mirror / Atom feed
* [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(&current_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     ` [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

* 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(&current_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

* 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

* 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

* 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

* 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

* [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(&current_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(&current_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:
&#149; 3 signs your SCM is hindering your productivity
&#149; Requirements for releasing software faster
&#149; 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

* 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:
&#149; 3 signs your SCM is hindering your productivity
&#149; Requirements for releasing software faster
&#149; 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(&current_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         ` [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

* 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

* 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

* 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

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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.