[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