Partitioned/clustered scheduling

Gedare Bloom gedare at rtems.org
Tue Nov 19 17:36:45 UTC 2013


On Tue, Nov 19, 2013 at 8:39 AM, Sebastian Huber
<sebastian.huber at embedded-brains.de> wrote:
> Hello,
>
> we would like to implement partitioned/clustered scheduling for SMP RTEMS.
> The reasons for this are highlighted in
>
> Björn B. Brandenburg, Scheduling and Locking in Multiprocessor Real-Time
> Operating Systems, 2011
>
> Partitioned/clustered scheduling means that the set of processors of a
> system can be partitioned into pairwise disjoint subsets.  Each subset of
> processors will be owned by one scheduler instance.
>
Great!

> The following proposal covers the processor configuration, high-level
> scheduler implementation and RTEMS API changes.
>
> ==== Scheduler Configuration ====
>
> There are two options for the scheduler instance configuration
>
> # static configuration by means of global data structures, and
> # configuration at run-time via function calls.
>
> For a configuration at run-time the system must start with a default
> scheduler.
> The global constructors are called in this environment.  The order of global
> constructor invocation is unpredictable so it is difficult to create threads
> in
> this context since the run-time scheduler configuration may not exist yet.
> Since scheduler data structures are allocated from the workspace the
> configuration must take a later run-time setup of schedulers into account
> for
> the workspace size estimate.  In case the default scheduler is not
> appropriate
> it must be replaced which gives raise to some implementation difficulties.
> Since the processor availability is determined by hardware constraints it is
> unclear which benefits a run-time configuration has.  For now run-time
> configuration of scheduler instances will be not implemented.
>
That's fine. I think scheduler run-time (re)configuration is
unnecessary except in some extreme cases for debugging.

> The focus is now on static configuration.  Every scheduler needs a control
> context.  The scheduler API must provide a macro which creates a global
> scheduler instance specific data structure with a designator name as a
> mandatory parameter.  The scheduler instance creation macro may require
> additional scheduler specific configuration options.  For example a
> fixed-priority scheduler instance must know the maximum priority level to
> allocate the ready chain control table.
>
> Once the scheduler instances are configured it must be specified for each
> processor in the system which scheduler instance owns this processor.
>
> For each processor except the initialization processor a scheduler instance
> is
> optional so that other operating systems can run independent of this RTEMS
> system on this processor.  It is a fatal error to omit a scheduler instance
> for
> the initialization processor.  The initialization processor is the processor
> which executes the boot_card() function.
>
>  /**
>   * @brief Processor configuration.
>   *
>   * Use RTEMS_CPU_CONFIG_INIT() to initialize this structure.
>   */
>  typedef struct {
>    /**
>     * @brief Scheduler instance for this processor.
>     *
>     * It is possible to omit a scheduler instance for this processor by
> using
>     * the @c NULL pointer.  In this case RTEMS will not use this processor
> and
>     * other operating systems may claim it.
>     */
>    Scheduler_Control *scheduler;
>  } rtems_cpu_config;
>
>  /**
>   * @brief Processor configuration initializer.
>   *
>   * @param scheduler The reference to a scheduler instance or @c NULL.
>   *
>   * @see rtems_cpu_config.
>   */
>  #define RTEMS_CPU_CONFIG_INIT(scheduler) \
>    { ( scheduler ) }
>
> Scheduler and processor configuration example:
>
>  RTEMS_SCHED_DEFINE_FP_SMP(sched_fp0, 256);
>  RTEMS_SCHED_DEFINE_FP_SMP(sched_fp1, 64);
>  RTEMS_SCHED_DEFINE_EDF_SMP(sched_edf0);
>
>  const rtems_cpu_config rtems_cpu_config_table[] = {
>    RTEMS_CPU_CONFIG_INIT(RTEMS_SCHED_REF_FP_SMP(sched_fp0)),
>    RTEMS_CPU_CONFIG_INIT(RTEMS_SCHED_REF_FP_SMP(sched_fp1)),
>    RTEMS_CPU_CONFIG_INIT(RTEMS_SCHED_REF_FP_SMP(sched_fp1)),
>    RTEMS_CPU_CONFIG_INIT(RTEMS_SCHED_REF_FP_SMP(sched_fp1)),
>    RTEMS_CPU_CONFIG_INIT(NULL),
>    RTEMS_CPU_CONFIG_INIT(NULL),
>    RTEMS_CPU_CONFIG_INIT(RTEMS_SCHED_REF_EDF_SMP(sched_edf0)),
>    RTEMS_CPU_CONFIG_INIT(RTEMS_SCHED_REF_EDF_SMP(sched_edf0)
>  };
>
>  const size_t rtems_cpu_config_count =
>    RTEMS_ARRAY_SIZE(rtems_cpu_config_table);
>
This looks good to me. I guess the user must define the table. We
should offer some logical/safe defaults, especially for
single-processor.

> An alternative to the processor configuration table would be to specify in
> the
> scheduler instance which processors are owned by the instance.  This would
> require a static initialization of CPU sets which is difficult.  Also the
> schedulers have to be registered somewhere, so some sort of table is needed
> anyway.  Since a processor can be owned by at most one scheduler instance
> this
> configuration approach enables an additional error source which is avoided
> by
> the processor configuration table.
>
> ==== Scheduler Implementation Changes ====
>
> Currently the scheduler operations have no control context and use global
> variables instead.  Thus the scheduler operations signatures must change to
> use
> a scheduler control context as the first parameter, e.g.
>
>  typedef struct Scheduler_Control Scheduler_Control;
>
>  typedef struct {
>    [...]
>    void ( *set_affinity )(
>      Scheduler_Control *self,
>      Thread_Control *thread,
>      size_t affinity_set_size,
>      const cpu_set_t *affinity_set
>    );
>    [...]
>  } Scheduler_Operations;
>
>  /**
>   * @brief General scheduler control.
>   */
>  struct Scheduler_Control {
>    /**
>     * @brief The scheduler operations.
>     */
>    Scheduler_Operations Operations;
>
>    /**
>     * @brief Size of the owned processor set in bytes.
>     */
>    size_t owned_cpu_set_size
>
>    /**
>     * @brief Reference to the owned processor set.
>     *
>     * A set bit means this processor is owned by this scheduler instance, an
>     * unset bit means the opposite.
>     */
>    cpu_set_t *owned_cpu_set;
>  };
>
For uniprocessor we don't need the owned processor set fields?

> Single processor configurations benefit also from this change since it makes
> all dependencies explicit and easier to access (allows more efficient
> machine
> code).
>
Good. This should eliminate the _Scheduler variable that is the
uniprocessor global scheduler handle? I never liked it.

> ==== RTEMS API Changes ====
>
> Each thread needs a processor affinity set in the RTEMS SMP configuration.
> The
> rtems_task_create() function will use the processor affinity set of the
> executing thread to initialize the processor affinity set of the created
> task.  This enables backward compatibility for existing software.
>
Good. What will be the default affinity of the init task?

> Two new functions should be added to alter and retrieve the processor
> affinity
> sets of threads.
>
>  /**
>   * @brief Sets the processor affinity set of a task.
>   *
>   * @param[in] task_id Identifier of the task.  Use @ref RTEMS_SELF to
> select
>   * the executing task.
>   * @param[in] affinity_set_size Size of the specified affinity set in
> bytes.
>   * This value must be positive.
>   * @param[in] affinity_set The processor affinity set for the task.  This
>   * pointer must not be @c NULL.  A set bit in the affinity set means that
> the
>   * task can execute on this processor and an unset bit means the opposite.
>   *
>   * @retval RTEMS_SUCCESSFUL Successful operation.
>   * @retval RTEMS_INVALID_ID Invalid task identifier.
>   * @retval RTEMS_INVALID_CPU_SET Invalid processor affinity set.
>   */
>  rtems_status_code rtems_task_set_affinity(
>    rtems_id task_id,
>    size_t affinity_set_size,
>    const cpu_set_t *affinity_set
>  );
>
>  /**
>   * @brief Gets the processor affinity set of a task.
>   *
>   * @param[in] task_id Identifier of the task.  Use @ref RTEMS_SELF to
> select
>   * the executing task.
>   * @param[in] affinity_set_size Size of the specified affinity set in
> bytes.
>   * This value must be positive.
>   * @param[out] affinity_set The processor affinity set of the task.  This
>   * pointer must not be @c NULL.  A set bit in the affinity set means that
> the
>   * task can execute on this processor and an unset bit means the opposite.
>   *
>   * @retval RTEMS_SUCCESSFUL Successful operation.
>   * @retval RTEMS_INVALID_ID Invalid task identifier.
>   * @retval RTEMS_INVALID_CPU_SET The affinity set is too small for the
>   * processor affinity set of the task.
>   */
>  rtems_status_code rtems_task_get_affinity(
>    rtems_id task_id,
>    size_t affinity_set_size,
>    cpu_set_t *affinity_set
>  );
>
These classic API set/get affinity functions seem reasonable. They
mimic existing affinity interfaces nicely. Now users should do
rtems_task_create(), rtems_task_set_affinity(), rtems_task_start() in
order to specify the affinity of their tasks, correct?

-Gedare

> --
> Sebastian Huber, embedded brains GmbH
>
> Address : Dornierstr. 4, D-82178 Puchheim, Germany
> Phone   : +49 89 189 47 41-16
> Fax     : +49 89 189 47 41-09
> E-Mail  : sebastian.huber at embedded-brains.de
> PGP     : Public key available on request.
>
> Diese Nachricht ist keine geschäftliche Mitteilung im Sinne des EHUG.
> _______________________________________________
> rtems-devel mailing list
> rtems-devel at rtems.org
> http://www.rtems.org/mailman/listinfo/rtems-devel




More information about the devel mailing list