[PATCH 1/7] xen/sched: introduce struct sched_ops as a shared scheduler vtable
Furkan Caliskan <[email protected]> Mon, 3 Aug 2026 08:06:08 +0300
| Newsgroups | org.xenproject.lists.xen-devel |
|---|---|
| Message-ID | <[email protected]> |
struct scheduler currently serves two purposes: it is the static vtable a scheduler backend defines (name, opt_name, sched_id, and all its function pointers), and it is also the per-cpupool runtime object scheduler_alloc() allocates. Being the same type forces scheduler_alloc() to memcpy() the whole vtable into a fresh allocation per cpupool, duplicating identical function pointers across every cpupool using the same scheduler. Introduce struct sched_ops to hold just the compile-time-constant identity and dispatch table, with no per-cpupool state, plus REGISTER_SCHED_OPS() and a sched_ops_array[] alongside the existing schedulers[]. scheduler_alloc(), sched_get_by_name(), and scheduler_init() are extended to also search sched_ops_array[], using a new sched_ops_to_scheduler() helper to build an identical flat struct scheduler regardless of which array a match came from. struct scheduler itself is left untouched for now and still duplicates every field sched_ops holds - this commit only lays the groundwork. Once every backend registers through sched_ops, struct scheduler will be shrunk to its genuinely per-cpupool fields (a pointer to a shared sched_ops instance, plus instance data), and the flattening copy, schedulers[], and REGISTER_SCHEDULER() will be removed. That is what actually removes the duplication; this commit does not yet change any behavior, since sched_ops_array[] is still empty. Signed-off-by: Furkan Caliskan <[email protected]> --- xen/arch/arm/xen.lds.S | 1 + xen/arch/ppc/xen.lds.S | 1 + xen/arch/riscv/xen.lds.S | 1 + xen/arch/x86/xen.lds.S | 1 + xen/common/sched/core.c | 118 +++++++++++++++++++++++++++++++++++-- xen/common/sched/private.h | 73 +++++++++++++++++++++++ xen/include/xen/xen.lds.h | 6 ++ 7 files changed, 196 insertions(+), 5 deletions(-) diff --git a/xen/arch/arm/xen.lds.S b/xen/arch/arm/xen.lds.S index 2d5f1c516d..9a63fa36a0 100644 --- a/xen/arch/arm/xen.lds.S +++ b/xen/arch/arm/xen.lds.S @@ -94,6 +94,7 @@ SECTIONS *(.data.page_aligned) SCHEDULER_ARRAY + SCHED_OPS_ARRAY HYPFS_PARAM *(.data .data.*) diff --git a/xen/arch/ppc/xen.lds.S b/xen/arch/ppc/xen.lds.S index d0f2ed43f1..da8f73d85b 100644 --- a/xen/arch/ppc/xen.lds.S +++ b/xen/arch/ppc/xen.lds.S @@ -85,6 +85,7 @@ SECTIONS *(.data.page_aligned) SCHEDULER_ARRAY + SCHED_OPS_ARRAY HYPFS_PARAM *(.data .data.*) diff --git a/xen/arch/riscv/xen.lds.S b/xen/arch/riscv/xen.lds.S index 65f136dce9..01f202e504 100644 --- a/xen/arch/riscv/xen.lds.S +++ b/xen/arch/riscv/xen.lds.S @@ -90,6 +90,7 @@ SECTIONS *(.data.page_aligned) SCHEDULER_ARRAY + SCHED_OPS_ARRAY HYPFS_PARAM *(.data .data.*) diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S index b9e888e596..d128a30440 100644 --- a/xen/arch/x86/xen.lds.S +++ b/xen/arch/x86/xen.lds.S @@ -307,6 +307,7 @@ SECTIONS *(.data.read_mostly) SCHEDULER_ARRAY + SCHED_OPS_ARRAY HYPFS_PARAM } PHDR(text) diff --git a/xen/common/sched/core.c b/xen/common/sched/core.c index 3609721426..fb2d1d5314 100644 --- a/xen/common/sched/core.c +++ b/xen/common/sched/core.c @@ -91,6 +91,10 @@ extern const struct scheduler *__start_schedulers_array[], *__end_schedulers_arr #define NUM_SCHEDULERS (__end_schedulers_array - __start_schedulers_array) #define schedulers __start_schedulers_array +extern const struct sched_ops *__start_sched_ops_array[], *__end_sched_ops_array[]; +#define NUM_SCHED_OPS (__end_sched_ops_array - __start_sched_ops_array) +#define sched_ops_array __start_sched_ops_array + static struct scheduler __read_mostly operations; static bool scheduler_active; @@ -98,6 +102,43 @@ static bool scheduler_active; static void sched_set_affinity( struct sched_unit *unit, const cpumask_t *hard, const cpumask_t *soft); + +static void sched_ops_to_scheduler( + struct scheduler *sched, const struct sched_ops *ops) +{ + sched->name = ops->name; + sched->opt_name = ops->opt_name; + sched->sched_id = ops->sched_id; + sched->global_init = ops->global_init; + sched->init = ops->init; + sched->deinit = ops->deinit; + sched->free_udata = ops->free_udata; + sched->alloc_udata = ops->alloc_udata; + sched->free_pdata = ops->free_pdata; + sched->alloc_pdata = ops->alloc_pdata; + sched->deinit_pdata = ops->deinit_pdata; + sched->alloc_domdata = ops->alloc_domdata; + sched->free_domdata = ops->free_domdata; + sched->switch_sched = ops->switch_sched; + sched->insert_unit = ops->insert_unit; + sched->remove_unit = ops->remove_unit; + sched->sleep = ops->sleep; + sched->wake = ops->wake; + sched->yield = ops->yield; + sched->context_saved = ops->context_saved; + sched->do_schedule = ops->do_schedule; + sched->pick_resource = ops->pick_resource; + sched->migrate = ops->migrate; + sched->adjust = ops->adjust; + sched->adjust_affinity = ops->adjust_affinity; +#ifdef CONFIG_SYSCTL + sched->adjust_global = ops->adjust_global; +#endif + sched->dump_settings = ops->dump_settings; + sched->dump_cpu_state = ops->dump_cpu_state; + sched->move_timers = ops->move_timers; +} + static struct sched_resource *cf_check sched_idle_res_pick(const struct scheduler *ops, const struct sched_unit *unit) { @@ -3004,11 +3045,27 @@ const struct scheduler *__init sched_get_by_name(const char *sched_name) return NULL; } +static inline +const struct sched_ops *__init sched_ops_get_by_name(const char* sched_name) +{ + unsigned int i; + for ( i = 0; i < NUM_SCHED_OPS; i++) + if ( sched_ops_array[i] && !strcmp(sched_ops_array[i]->opt_name, sched_name) ) + return sched_ops_array[i]; + + return NULL; +} + int __init sched_get_id_by_name(const char *sched_name) { const struct scheduler *scheduler = sched_get_by_name(sched_name); + const struct sched_ops *ops; + + if ( scheduler ) + return scheduler->sched_id; - return scheduler ? scheduler->sched_id : -1; + ops = sched_ops_get_by_name(sched_name); + return ops ? ops->sched_id : -1; } /* Initialise the data structures. */ @@ -3016,6 +3073,7 @@ void __init scheduler_init(void) { struct domain *idle_domain; const struct scheduler *scheduler; + const struct sched_ops *ops; int i; scheduler_enable(); @@ -3048,15 +3106,52 @@ void __init scheduler_init(void) } } + for ( i = 0; i < NUM_SCHED_OPS; i++) + { +#define sched_test_func(f) \ + if ( !sched_ops_array[i]->f ) \ + { \ + printk("scheduler %s misses .%s, dropped\n", \ + sched_ops_array[i]->opt_name, #f); \ + sched_ops_array[i] = NULL; \ + } + + sched_test_func(init); + sched_test_func(deinit); + sched_test_func(pick_resource); + sched_test_func(alloc_udata); + sched_test_func(free_udata); + sched_test_func(switch_sched); + sched_test_func(do_schedule); + +#undef sched_test_func + + if ( sched_ops_array[i]->global_init && sched_ops_array[i]->global_init() < 0) + { + printk("scheduler %s failed initialization, dropped\n", + sched_ops_array[i]->opt_name); + sched_ops_array[i] = NULL; + } + } + scheduler = sched_get_by_name(opt_sched); - if ( !scheduler ) + ops = scheduler ? NULL : sched_ops_get_by_name(opt_sched); + if ( !scheduler && !ops ) { printk("Could not find scheduler: %s\n", opt_sched); scheduler = sched_get_by_name(CONFIG_SCHED_DEFAULT); - BUG_ON(!scheduler); - printk("Using '%s' (%s)\n", scheduler->name, scheduler->opt_name); + ops = scheduler ? NULL : sched_ops_get_by_name(CONFIG_SCHED_DEFAULT); + BUG_ON(!scheduler && !ops); + if ( scheduler ) + printk("Using '%s' (%s)\n", scheduler->name, scheduler->opt_name); + else + printk("Using '%s' (%s)\n", ops->name, ops->opt_name); } - operations = *scheduler; + + if ( scheduler ) + operations = *scheduler; + else + sched_ops_to_scheduler(&operations, ops); if ( cpu_schedule_up(0) ) BUG(); @@ -3415,12 +3510,25 @@ struct scheduler *scheduler_alloc(unsigned int sched_id) for ( i = 0; i < NUM_SCHEDULERS; i++ ) if ( schedulers[i] && schedulers[i]->sched_id == sched_id ) goto found; + + for ( i = 0; i < NUM_SCHED_OPS; i++ ) + if ( sched_ops_array[i] && sched_ops_array[i]->sched_id == sched_id ) + goto found_new; + return ERR_PTR(-ENOENT); found: if ( (sched = xmalloc(struct scheduler)) == NULL ) return ERR_PTR(-ENOMEM); memcpy(sched, schedulers[i], sizeof(*sched)); + goto init; + + found_new: + if ( (sched = xzalloc(struct scheduler)) == NULL ) + return ERR_PTR(-ENOMEM); + sched_ops_to_scheduler(sched, sched_ops_array[i]); + + init: if ( (ret = sched_init(sched)) != 0 ) { xfree(sched); diff --git a/xen/common/sched/private.h b/xen/common/sched/private.h index d6884550cd..4dd5c99b87 100644 --- a/xen/common/sched/private.h +++ b/xen/common/sched/private.h @@ -294,6 +294,76 @@ static inline spinlock_t *pcpu_schedule_trylock(unsigned int cpu) return NULL; } +struct sched_ops { + const char *name; /* full name for this sched_ops */ + const char *opt_name; /* option name for this sched_ops */ + unsigned int sched_id; /* ID for this sched_ops */ + + int (*global_init) (void); + + int (*init) (struct scheduler *ops); + void (*deinit) (struct scheduler *ops); + + void (*free_udata) (const struct scheduler *ops, void *priv); + void * (*alloc_udata) (const struct scheduler *ops, + struct sched_unit *unit, void *dd); + + void (*free_pdata) (const struct scheduler *ops, + void *pcpu, int cpu); + void * (*alloc_pdata) (const struct scheduler *ops, int cpu); + void (*deinit_pdata) (const struct scheduler *ops, + void *pcpu, int cpu); + + /* Returns ERR_PTR(-err) for error, NULL for 'nothing needed'. */ + void * (*alloc_domdata) (const struct scheduler *ops, + struct domain *dom); + /* Idempotent. */ + void (*free_domdata) (const struct scheduler *ops, void *data); + + spinlock_t * (*switch_sched) (struct scheduler *new_ops, unsigned int cpu, + void *pdata, void *vdata); + + /* Activate / deactivate units in a cpu pool */ + void (*insert_unit) (const struct scheduler *ops, + struct sched_unit *unit); + void (*remove_unit) (const struct scheduler *ops, + struct sched_unit *unit); + + void (*sleep) (const struct scheduler *ops, + struct sched_unit *unit); + void (*wake) (const struct scheduler *ops, + struct sched_unit *unit); + void (*yield) (const struct scheduler *ops, + struct sched_unit *unit); + void (*context_saved) (const struct scheduler *ops, + struct sched_unit *unit); + + void (*do_schedule) (const struct scheduler *ops, + struct sched_unit *currunit, s_time_t now, + bool tasklet_work_scheduled); + + struct sched_resource *(*pick_resource)(const struct scheduler *ops, + const struct sched_unit *unit); + void (*migrate) (const struct scheduler *ops, + struct sched_unit *unit, + unsigned int new_cpu); + int (*adjust) (const struct scheduler *ops, + struct domain *d, + struct xen_domctl_scheduler_op *op); + void (*adjust_affinity)(const struct scheduler *ops, + struct sched_unit *unit, + const struct cpumask *hard, + const struct cpumask *soft); +#ifdef CONFIG_SYSCTL + int (*adjust_global) (const struct scheduler *ops, + struct xen_sysctl_scheduler_op *sc); +#endif + void (*dump_settings) (const struct scheduler *ops); + void (*dump_cpu_state) (const struct scheduler *ops, int cpu); + void (*move_timers) (const struct scheduler *ops, + struct sched_resource *sr); +}; + struct scheduler { const char *name; /* full name for this scheduler */ const char *opt_name; /* option name for this scheduler */ @@ -546,6 +616,9 @@ static inline void sched_unit_unpause(const struct sched_unit *unit) #define REGISTER_SCHEDULER(x) static const struct scheduler *x##_entry \ __used_section(".data.schedulers") = &(x) +#define REGISTER_SCHED_OPS(x) static const struct sched_ops *x##_entry \ + __used_section(".data.sched_ops") = &(x) + struct cpupool { unsigned int cpupool_id; diff --git a/xen/include/xen/xen.lds.h b/xen/include/xen/xen.lds.h index ea11e3fb62..157d48eabd 100644 --- a/xen/include/xen/xen.lds.h +++ b/xen/include/xen/xen.lds.h @@ -179,6 +179,12 @@ *(.data.schedulers) \ __end_schedulers_array = .; +#define SCHED_OPS_ARRAY \ + . = ALIGN(POINTER_ALIGN); \ + __start_sched_ops_array = .; \ + *(.data.sched_ops) \ + __end_sched_ops_array = .; + #ifdef CONFIG_HYPFS #define HYPFS_PARAM \ . = ALIGN(POINTER_ALIGN); \ -- 2.34.1