TimerDelegate(), TimerDelegate_r()

QNX SDP8.0C Library ReferenceAPIDeveloper

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.

CAUTION:
If you set _NTO_TIMER_DELEGATE as the action, TimerDelegate() blocks if it has to wait for the clock IST to finish running. Once the function successfully returns, the thread that called it must call TimerDelegate() again with the _NTO_TIMER_RECLAIM action before performing any blocking operation or causing a context switch to a thread of lower priority.

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:

On success, DELEGATE and RECLAIM return EOK, and QUERY returns a CPU number. On failure;
  • 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:

QNX OS

Safety:
Cancellation point No
Signal handler No
Thread Yes
Page updated: