Skip to content

Scheduler

include/sch/sched.h View source View on GitHub
struct idle_thread_data (2 fields) source
§ atomic_uint64_t last_entry_ms
§ uint64_t last_exit_ms
struct scheduler (32 fields) source

Current tick data

§ atomic_bool tick_enabled
§ time_ms_t tick_duration_ms

Structures

§ struct list_head urgent_threads
§ struct rbt thread_rbt
§ struct rbt completed_rbt
§ struct list_head rt_threads
§ struct list_head bg_threads
§ struct rbt climb_threads

threads on this CPU participating in CLIMB

§ atomic_uint8_t queue_bitmap
§ struct thread *current
§ struct thread *drop_last_ref
§ struct timer tick
§ struct spinlock wroost_lock
§ struct wroost_percpu_data *wroost_data
§ size_t thread_count[THREAD_PRIO_CLASS_COUNT]

Thread count at each prio

§ size_t total_thread_count
§ size_t total_weight

Period information

§ bool period_enabled
§ uint64_t current_period
§ time_ms_t period_ms
§ time_ms_t period_start_ms

Timestamp

§ cpu_id_t core_id

Work steal/migration

§ atomic_bool being_robbed
TODO

Rework time load balancing away from this foolery

§ atomic_bool stealing_work
§ struct spinlock lock

Idle thread data

§ struct thread *idle_thread
§ struct idle_thread_data idle_thread_data
§ struct scheduler *other_locked

If we acquired the lock of another scheduler in scheduler_yield(), we store a pointer to it here.

If this is NULL, we didn’t do that, but in the case that it isn’t, we must drop the raw lock for this in addition to the lock for our scheduler

§ enum irql locked_irql
§ enum irql other_locked_irql
§ struct cpu_mask deferred_resched_mask
§ struct rt_scheduler_percpu *rt
struct scheduler_data (4 fields) source

For a global structure containing central scheduler data

§ uint32_t max_concurrent_stealers
§ atomic_uint32_t active_stealers
§ atomic_uint32_t total_threads
§ atomic_int64_t steal_min_diff
void scheduler_init(void)
source
void scheduler_debug_start(void)
source
void scheduler_debug_start(void)
source
struct scheduler * scheduler_select_best_for_thread(struct thread *t)
source
void scheduler_add_thread(
    struct scheduler *sched,
    struct thread *thread
)
source
void scheduler_remove_thread(
    struct scheduler *sched,
    struct thread *t
)
source
void schedule(void)
source
void k_sch_main(void * *)
source
void scheduler_idle_main(void * *)
source
void scheduler_yield(void)
source
void scheduler_period_start(struct scheduler *s, uint64_t now_ms)
source
void switch_context(struct cpu_context *old, struct cpu_context *new)
source
void load_context(struct cpu_context *new)
source
void save_context(struct cpu_context *new)
source
bool scheduler_can_steal_work(struct scheduler *sched)
source
bool scheduler_can_take_thread(size_t core, struct thread *target)
source
uint64_t scheduler_compute_steal_threshold(void)
source
struct thread * scheduler_try_do_steal(struct scheduler *sched)
source
struct scheduler * scheduler_pick_victim(struct scheduler *self)
source
struct thread * scheduler_steal_work(
    struct scheduler *new,
    struct scheduler *victim
)
source
size_t scheduler_try_push_to_idle_core(struct scheduler *sched)
source
void scheduler_tick_enable(void)
source
void scheduler_tick_disable(void)
source
enum irq_result scheduler_reschedule_isr(
    struct irq_context *rsp,
    void *ctx
)
source
bool scheduler_self_in_resched(void)
source
bool scheduler_mark_self_in_resched(bool new)
source

Because this is always executed at or above DISPATCH it is fine

bool scheduler_mark_core_needs_resched(struct core *c, bool new)
source
bool scheduler_mark_self_needs_resched(bool new)
source
bool scheduler_mark_core_needs_run_dpcs(struct core *c, bool new)
source
bool scheduler_mark_self_needs_run_dpcs(bool new)
source
void scheduler_mark_self_idle(bool new)
source

this is only ever called when a thread is loaded

void scheduler_resched_if_needed(void)
source
bool scheduler_core_idle(struct core *c)
source
void scheduler_force_resched(struct scheduler *sched)
source
NOTE

this function is unsafe to call from context that holds a runqueue lock. The reason:

Imagine the following:     CPU 0 is holding RQ 0’s lock     CPU 0 IPI’s CPU 1     CPU 1, before that IPI arrives, was spinning on            RQ 0’s lock, since it was migrating a thread.

           Thus, CPU 1 cannot respond to the IPI            because it has interrupts disabled,            and will never enter the ISR to ack.

    CPU 0 spins on waiting for this (uncompletable) IPI to complete     CPU 1 is also stuck waiting for RQ 0’s lock to be relinquished,           which is something that won’t happen because of this           dependency

    boom deadlock.

    scheduler core code is supposed to call scheduler_defer_resched()     defined in the internal header there.

bool cc_warn_unused_result scheduler_try_force_resched(struct scheduler *sched)
source
void scheduler_force_run_dpcs(cpu_id_t cpu)
source

Uses the blocking ipi_send(), so no spinlock may be held.

bool scheduler_preemption_disabled(enum topology_caller c)
source
void scheduler_change_tick_duration(uint64_t new_duration)
source
extern struct scheduler_data scheduler_data
source
#define WORK_STEAL_THRESHOLD
source
Expansion
#define WORK_STEAL_THRESHOLD \
    75ULL /* How little work the core needs to be \
           * doing to try to steal work from another \
           * core. This means "% of the average" \
           */
#define SCHEDULER_DEFAULT_WORK_STEAL_MIN_DIFF 130
source