TimerDelegate(), TimerDelegate_r()
Delegate timers between CPUs
Synopsis:
#include <sys/neutrino.h>
int TimerDelegate( uint32_t action,
int32_t owner_cpu,
int32_t delegate_cpu );
int TimerDelegate_r( uint32_t action,
int32_t owner_cpu,
int32_t delegate_cpu );
Arguments:
- action
- The action to perform. One of the following:
- _NTO_TIMER_DELEGATE — Delegate a CPU's timers to a delegate CPU.
- _NTO_TIMER_RECLAIM — Reclaim a CPU's timers from a delegate CPU.
- _NTO_TIMER_QUERY_DELEGATE — Query the kernel to determine a CPU's current delegate CPU.
- owner_cpu
- The CPU the call originated from, or -1 to use the current CPU. The owner_cpu can't be a CPU that's acting on the behalf of another CPU.
- delegate_cpu
- The delegate CPU. The acceptable values depend on the
action you specified:
- DELEGATE — You can't specify a CPU that has timers delegated to another CPU.
- RECLAIM — Either specify the CPU that's taking care of the timers from owner_cpu, or set this field to -1 to let the kernel decide.
- QUERY — Ignored.
Library:
libc
Use the -l c option to qcc to link against this library. This library is usually included automatically.
Description:
The TimerDelegate() and TimerDelegate_r()
functions move the handling of timers between processors. This allows one processor
to temporarily delegate (i.e., give ownership of) its timers to another processor,
and then reclaim (i.e., take back the ownership of) these timers. Software timer
queues are associated with a CPU in the QNX OS; the purpose of these functions is to
delegate a CPU's timers to another CPU allowing you to take a CPU offline and keep
its assigned timers firing. (For more information, go to CPU Offlining
in the QNX OS
System Architecture Guide.)
The TimerDelegate() and TimerDelegate_r() functions are identical, except in the way they indicate errors. Refer to the "Returns" section for details. Privileged applications responsible for offlining and onlining CPUs, such as power managers, should call these functions.
Only one thread can call TimerDelegate() at a time. The function returns EBUSY if multiple threads attempt to call the function concurrently. The thread calling this function can loop on EBUSY until the function returns another return code.
Delegating a CPU's timer to another CPU
You should delegate a CPU's timers before taking the CPU offline. To do so, call TimerDelegate() with the _NTO_TIMER_DELEGATE action to delegate a CPU's timers. The application then proposes a delegate CPU candidate. The kernel can refuse the proposed CPU if the CPU isn't handling its own timers at that moment (i.e., the proposed CPU already delegated its timers to another CPU).
If the kernel accepts the proposed CPU, TimerDelegate() takes care of stopping the timer hardware of the owner (delegator) CPU.
Reclaiming a CPU's timer from another CPU
Once a CPU is back in operation, you should ensure that it reclaims its timers before the CPU notifies the kernel that it's ready to take part in scheduling. To do so, call TimerDelegate() with the _NTO_TIMER_RECLAIM action to reclaim a CPU's timers. If the application is responsible for tracking the delegate CPU, the application passes on this information to the kernel. Otherwise, the kernel must find the delegate CPU itself (set delegate_cpu to -1), but this is costly.
The CPU only reclaims its own timers, which excludes any timers that it had previously been delegated with as a delegate CPU (refer to the section below on "Transitive Delegation" for more information).
Transitive Delegation
When a CPU delegates its timers with a delegate CPU, all timers in its care go to the delegate CPU. When the CPU comes back online, it only reclaims its own timers. The delegate CPU takes care of the timers the CPU was previously delegated with before going offline.
For example, imagine a system with four CPUs. CPU3 needs to be taken offline and has previously been delegated with CPU2's timers. CPU3 delegates its timers to CPU1. CPU1 is then responsible for the timers of both CPU2 and CPU3.
If CPU3 comes back online before CPU2, then CPU3 is only responsible for its own timers from that point on. Subsequently, CPU1 is responsible for its own timers and CPU2's timers, until CPU2 reclaims its timers.
Requirements
The TimerDelegate*() functions only succeed if the following requirements are met:
- The privileged application can run a thread at a higher priority that the
clock IST (254 by default) and a lower priority than the IPI IST (always
255). Therefore, you must configure the system so that the clock IST runs at
a maximum priority of 253, by passing the -C option to
procnto as follows:
procnto-smp-instr -C0,253 - The process must have the runstate ability enabled. For more
information, go to
Abilities Reference
in the QNX OS System Security Guide. - The thread that calls the function must:
- be pinned to the CPU delegating its timers.
- have its scheduling policy set to SCHED_OFFLINING. This policy is a FIFO scheduling policy that allows a thread to be of higher priority than the clock IST.
- have a higher priority than the clock IST.
Returns:
- TimerDelegate() returns -1 and sets errno.
- TimerDelegate_r() returns the negative of a value from the Errors section and doesn't set errno.
Errors:
- EAGAIN
- The kernel refused the proposed delegate_cpu during a
DELEGATE operation. For more information, refer to the
Delegating a CPU's timer to another CPU
section. - EBUSY
- Multiple threads tried to call TimerDelegate() concurrently.
- EINVAL
- An invalid CPU was provided.
- ENOEXEC
- The calling thread doesn't have its scheduling policy set to SCHED_OFFLINING.
- ENOSYS
- An invalid action was provided, or the CPU provided isn't the current CPU.
- ENOTSUP
- The call was performed on a uniprocessor system.
- EPERM
- The application process didn't have the PROCMGR_AID_RUNSTATE ability enabled.
- ERANGE
- The calling thread's priority is less than or equal to that of the clock IST.
- ESRCH
- The kernel couldn't find the current delegate CPU (i.e., the current CPU hasn't delegated its timers).
Examples:
...
// Delegate this CPU's timers to CPU 0
int delegate_cpu = 0;
int delegate_rc;
int offline_rc = EOK;
do {
delegate_rc = TimerDelegate_r(_NTO_TIMER_DELEGATE, -1, delegate_cpu);
} while (delegate_rc == -EBUSY);
if (delegate_rc == EOK) {
// Take this CPU offline
offline_rc = SchedCtl_r(SCHED_PROCESSOR_OFFLINE, NULL, 0);
}
if (delegate_rc != EOK || offline_rc != EOK) {
// Lower the calling thread's priority and handle the error
...
}
...
...
// Reclaim timers from CPU 0
int delegate_cpu = 0;
int reclaim_rc;
do {
reclaim_rc = TimerDelegate_r(_NTO_TIMER_RECLAIM, -1, delegate_cpu);
} while (reclaim_rc == -EBUSY);
// Bring this CPU back online
int online_rc = SchedCtl_r(SCHED_PROCESSOR_ONLINE, NULL, 0);
if (reclaim_rc != EOK || online_rc != EOK) {
// Lower the calling thread's priority and handle the error
...
}
...
...
// Reclaim timers in case transitive delegation is possible
int reclaim_rc;
do {
reclaim_rc = TimerDelegate_r(_NTO_TIMER_RECLAIM, -1, -1);
} while (reclaim_rc == -EBUSY);
// Bring this CPU back online
int online_rc = SchedCtl_r(SCHED_PROCESSOR_ONLINE, NULL, 0);
if (reclaim_rc != EOK || online_rc != EOK) {
// Lower the calling thread's priority and handle the error
...
}
...Classification:
| Safety: | |
|---|---|
| Cancellation point | No |
| Signal handler | No |
| Thread | Yes |
