casaroli opened a new pull request, #21424:
URL: https://github.com/apache/nuttx/pull/21424

   ## Summary
   
   NuttX sets `_POSIX_CPUTIME` and `_POSIX_THREAD_CPUTIME` with 
`CONFIG_SCHED_CRITMONITOR`, and `clock_gettime()` reads 
`CLOCK_PROCESS_CPUTIME_ID` and `CLOCK_THREAD_CPUTIME_ID`. But `timer_create()` 
gave `EINVAL` for these clocks, and `setitimer()` gave `EINVAL` for 
`ITIMER_VIRTUAL` and `ITIMER_PROF`. The CPU-time clocks also lost time: the CPU 
time of a running thread did not change until it stopped to run, a thread on 
another CPU lost its CPU time with `CONFIG_SCHED_CPULOAD_CRITMONITOR`, and the 
process CPU time lost the time of threads that exited.
   
   The PR has six commits:
   
   - `sched/clock`: `nxsched_critmon_runtime()` adds the time since the thread 
started to run if the thread runs now. `nxsched_switch_critmon()` sets 
`run_start` to zero when a thread stops to run: the scheduler changes 
`task_state` before the context switch, so `task_state` alone cannot tell which 
time slice is not counted yet. `clock_gettime()` uses `perf_convert()`, which 
matches `perf_gettime()` also on boards without a perf counter.
   - `sched/critmon`: with `CONFIG_SCHED_CPULOAD_CRITMONITOR` on SMP, a context 
switch on one CPU set `run_start` of the running threads of the other CPUs, but 
did not add the elapsed time to their `run_time`. The CPU load now keeps its 
own time per CPU (`g_cpuload_start[]`), and only the CPU of a thread changes 
its `run_start` and `run_time`. `nxsched_update_critmon()` (from 
`clock_cpuload()`) does not change them any more. Each running thread gets its 
own elapsed CPU load ticks.
   - `sched/clock`: `group_leave()` adds the run time of a thread that leaves 
the group to `tg_run_time`, and the process CPU-time clock adds `tg_run_time`.
   - `sched/timer`: `timer_create()` takes the CPU-time clocks, also the clocks 
of `clock_getcpuclockid()` and `pthread_getcpuclockid()`. The timer keeps its 
expiration time on the CPU-time clock. Its watchdog expires at the first time 
at which the CPU time can reach it (a thread clock does not run faster than 
real time, a process clock not faster than `CONFIG_SMP_NCPUS` times real time). 
The watchdog then reads the clock and starts again for the remaining time if 
necessary. The scheduler does not change, and tick and tickless builds work the 
same. `timer_gettime()` returns the remaining CPU time, and a periodic timer 
counts its overruns in CPU time.
   - `sched/timer`: `setitimer()` and `getitimer()` take `ITIMER_VIRTUAL` 
(`SIGVTALRM`) and `ITIMER_PROF` (`SIGPROF`) on the process CPU-time clock. 
NuttX does not count user time and system time separately, so both count all 
the CPU time of the process.
   - `Documentation`: `timer_create()` in the clocks and timers reference, and 
the `_POSIX_CPUTIME` and XSI timer tables of `posix.rst`.
   
   ## Impact
   
   Only builds with the CPU-time clocks (`CONFIG_SCHED_CRITMONITOR` with 
`CONFIG_SCHED_CRITMONITOR_MAXTIME_THREAD >= 0`) change. Without them the new 
code does not build: nucleo-l432kc:nsh has the same size as on master (text 
250764, data 1048, bss 8824). With `CONFIG_SCHED_CRITMONITOR`, 
nucleo-l432kc:nsh gets 1828 bytes more text, 4 bytes less data and 24 bytes 
more bss.
   
   The timers use only the watchdog and `nxclock_gettime()`, so flat, protected 
and kernel builds, SMP and tickless builds use the same code. `struct 
task_group_s` keeps one interval timer per `ITIMER_*` value (three pointers 
with the CPU-time clocks, one as before without them) and, with the CPU-time 
clocks and pthreads, `tg_run_time`.
   
   `CONFIG_SCHED_CPULOAD_CRITMONITOR` on SMP: the CPU load of a thread on 
another CPU is now its own elapsed time since the last update of that CPU, not 
the elapsed time of the thread that stops to run on this CPU.
   
   In the flat build too, `timer_create()` on a CPU-time clock and 
`setitimer()` with `ITIMER_VIRTUAL` or `ITIMER_PROF` now succeed where they 
returned `EINVAL`, and `clock_gettime()` on a CPU-time clock now gives larger 
(correct) values.
   
   ## Testing
   
   A test program `timercpu.c` (25 checks; it gives the same results on Linux 
with 2 CPUs): the thread CPU time in a busy loop; `EINVAL` for an invalid 
clock; a process timer and a thread timer do not expire while the thread sleeps 
or while another thread runs, and expire after the CPU time; `timer_gettime()` 
counts down CPU time; overruns of a periodic timer; `timer_delete()` of an 
armed timer; `setitimer()` with `ITIMER_VIRTUAL` and `ITIMER_PROF`; the process 
CPU time after a thread that used 300 ms exited; with 2 CPUs or more, the CPU 
time of a thread on CPU 1 that runs 500 ms while CPU 0 switches threads every 
millisecond. A second program `timercpu2.c` arms a process timer and a thread 
timer and waits in a busy loop without system calls. All with 
`CONFIG_SCHED_CRITMONITOR`:
   
   | Configuration | master | this PR |
   |---|---|---|
   | qemu-armv8a:knsh (kernel build, arm64, HVF, 1 CPU) | FAIL: busy loop CPU 
time 0 ms, `timer_create()` `EINVAL`, exited thread 0 ms | 24/24 PASS (the CPU 
1 check needs SMP), timercpu2 PASS |
   | rv-virt:ksmp64 (kernel build, rv64, 4 CPUs, QEMU TCG, 
`CONFIG_SCHED_CPULOAD_CRITMONITOR`) | FAIL: the same, and the thread on CPU 1 
has 1 ms of CPU time | 25/25 PASS (exited thread 300 ms, thread on CPU 1 500 
ms) |
   | rv-virt:ksmp64 with only the first two commits | - | the thread on CPU 1 
PASS (500 ms), the exited thread FAIL (0 ms) |
   
   A cmocka test for nuttx-apps (a PR that follows this one; qemu-armv8a:nsh, 
flat build): 10 of 12 cases FAIL on master; with this PR 11 PASS and the SMP 
case is skipped.
   
   LTP (testing/ltp, qemu-armv8a:nsh): `timer_create` 10-1 and 11-1 and 
`pthread_create` 11-1 failed on master with `EINVAL`; with this PR they PASS, 
and `timer_create` 1-1, `timer_gettime` 1-1, `clock_gettime` 4-1 and 
`clock_getcpuclockid` 1-1 still PASS.
   
   ostest exits with status 0 on qemu-armv8a:knsh, qemu-armv8a:nsh and 
rv-virt:ksmp64, on master and with this PR. After the rebase on master 
dcaf0be558a, qemu-armv8a:knsh and rv-virt:ksmp64 ran again with the same 
results.
   
   On qemu-armv8a the CPU-time clocks need `CONFIG_ARCH_PERF_EVENTS`, and 
`qemu_boot.h` gives `up_perf_init()` 62.5 MHz while QEMU counts `PMCCNTR_EL0` 
at 1 GHz (the clocks run 16 times too fast); the arm64 runs above used 1 GHz in 
the test tree, and this PR does not change it. Also the PMU counter is started 
only on the boot CPU, so on qemu-armv8a SMP the CPU time of threads on the 
other CPUs does not count (on master too); the SMP results are from rv-virt, 
where the perf time is the global timer.
   
   Build with `EXTRAFLAGS="-Wno-cpp -Werror"` and `CONFIG_SCHED_CRITMONITOR`, 
each OK: nucleo-l432kc:nsh (arm, flat), mps2-an500:knsh (arm, protected), 
qemu-armv8a:knsh (arm64, kernel), qemu-armv8a:nsh_smp_tickless (arm64, SMP, 
tickless), rv-virt:knsh64 (rv64, kernel), rv-virt:pnsh64 (rv64, protected); 
with `CONFIG_SCHED_CPULOAD_CRITMONITOR` also nucleo-l432kc:nsh and 
qemu-armv8a:nsh_smp (with `CONFIG_SCHED_CRITMONITOR_MAXTIME_PREEMPTION=0` and 
`_CSECTION=0`). nucleo-l432kc:nsh without `CONFIG_SCHED_CRITMONITOR` is OK too.
   
   `tools/checkpatch.sh -c -u -m -g` passes.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to