Replace the blocking notifier chain with an explicit per-type callback table (struct housekeeping_cbs). Each subsystem registers callbacks at initcall time; pre_validate() runs before the RCU pointer swap to allow rejecting the update, and apply() runs after synchronize_rcu() when the new mask is visible to readers.
The table is limited to HK_MAX_CBS (4) slots per type, sufficient for the kernel-noise subsystems and avoiding unbounded dynamic allocation in the update path. The interface provides deterministic callback order and explicit registration, giving each subsystem maintainer clear visibility into when and why its callback is invoked — unlike the opaque priority-based dispatch of notifier chains. Signed-off-by: Jing Wu <[email protected]> Signed-off-by: Qiliang Yuan <[email protected]> --- include/linux/sched/isolation.h | 31 +++++++++++++++ kernel/sched/isolation.c | 87 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/include/linux/sched/isolation.h b/include/linux/sched/isolation.h index cf0fd03dd7a24..f362876b3ebdf 100644 --- a/include/linux/sched/isolation.h +++ b/include/linux/sched/isolation.h @@ -46,6 +46,33 @@ extern bool housekeeping_test_cpu(int cpu, enum hk_type type); extern int housekeeping_update(struct cpumask *isol_mask); extern void __init housekeeping_init(void); +/** + * struct housekeeping_cbs - Per-subsystem callbacks for housekeeping mask changes + * @name: Subsystem name for diagnostic messages + * @pre_validate: Run before RCU pointer swap. Return -EINVAL + * to reject the update. + * @apply: Run after synchronize_rcu(). Reconfigure subsystem + * state. The new mask is visible to readers. + * + * Register subsystem callbacks at initcall time. + * Invoke callbacks in registration order when the corresponding + * housekeeping mask changes. Skip types not present in the update + * mask. + * + * Replace the notifier-chain pattern with deterministic callback + * ordering. + */ +struct housekeeping_cbs { + const char *name; + int (*pre_validate)(enum hk_type type, + const struct cpumask *cur_mask, + const struct cpumask *new_mask); + void (*apply)(enum hk_type type); +}; + +int housekeeping_register_cbs(enum hk_type type, struct housekeeping_cbs *cbs); +int housekeeping_unregister_cbs(enum hk_type type, struct housekeeping_cbs *cbs); + #else static inline int housekeeping_any_cpu(enum hk_type type) @@ -73,6 +100,10 @@ static inline bool housekeeping_test_cpu(int cpu, enum hk_type type) static inline int housekeeping_update(struct cpumask *isol_mask) { return 0; } static inline void housekeeping_init(void) { } +static inline int housekeeping_register_cbs(enum hk_type type, + struct housekeeping_cbs *cbs) { return 0; } +static inline int housekeeping_unregister_cbs(enum hk_type type, + struct housekeeping_cbs *cbs) { return 0; } #endif /* CONFIG_CPU_ISOLATION */ static inline bool housekeeping_cpu(int cpu, enum hk_type type) diff --git a/kernel/sched/isolation.c b/kernel/sched/isolation.c index ef152d401fe20..aae4dff7fbfc8 100644 --- a/kernel/sched/isolation.c +++ b/kernel/sched/isolation.c @@ -28,6 +28,93 @@ struct housekeeping { static struct housekeeping housekeeping; +/* + * Maintain an explicit callback table indexed by housekeeping type. + * Invoke callbacks for affected types in deterministic order: + * pre_validate() before the RCU pointer swap, apply() after + * synchronize_rcu(). + */ +#define HK_MAX_CBS 4 + +static struct { + struct housekeeping_cbs *cbs[HK_MAX_CBS]; + int nr; +} housekeeping_cbs_table[HK_TYPE_MAX]; + +/** + * housekeeping_register_cbs - Register explicit callbacks for a housekeeping type + * @type: Housekeeping type to register for + * @cbs: Callback structure containing pre_validate() and apply() + * + * Callbacks run in registration order when the mask for @type changes: + * pre_validate() before the RCU swap may reject the update; apply() + * after synchronize_rcu() reconfigures subsystem state. + * + * Return: 0 on success, -EINVAL if @type or @cbs is invalid, + * -ENOSPC if the per-type table is full. + */ +int housekeeping_register_cbs(enum hk_type type, struct housekeeping_cbs *cbs) +{ + if (type >= HK_TYPE_MAX || !cbs) + return -EINVAL; + if (housekeeping_cbs_table[type].nr >= HK_MAX_CBS) + return -ENOSPC; + housekeeping_cbs_table[type].cbs[housekeeping_cbs_table[type].nr++] = cbs; + return 0; +} +EXPORT_SYMBOL_GPL(housekeeping_register_cbs); + +/** + * housekeeping_unregister_cbs - Remove previously registered callbacks + * @type: Housekeeping type + * @cbs: Callback structure to remove + * + * Return: 0 on success, -EINVAL if arguments are invalid, + * -ENOENT if @cbs was not registered. + */ +int housekeeping_unregister_cbs(enum hk_type type, struct housekeeping_cbs *cbs) +{ + int i; + + if (type >= HK_TYPE_MAX || !cbs) + return -EINVAL; + for (i = 0; i < housekeeping_cbs_table[type].nr; i++) { + if (housekeeping_cbs_table[type].cbs[i] == cbs) { + housekeeping_cbs_table[type].cbs[i] = + housekeeping_cbs_table[type].cbs[--housekeeping_cbs_table[type].nr]; + return 0; + } + } + return -ENOENT; +} +EXPORT_SYMBOL_GPL(housekeeping_unregister_cbs); + +static int housekeeping_pre_validate_cbs(enum hk_type type, + const struct cpumask *cur, + const struct cpumask *new) +{ + int i, ret; + + for (i = 0; i < housekeeping_cbs_table[type].nr; i++) { + if (!housekeeping_cbs_table[type].cbs[i]->pre_validate) + continue; + ret = housekeeping_cbs_table[type].cbs[i]->pre_validate(type, cur, new); + if (ret < 0) + return ret; + } + return 0; +} + +static void housekeeping_apply_cbs(enum hk_type type) +{ + int i; + + for (i = 0; i < housekeeping_cbs_table[type].nr; i++) { + if (housekeeping_cbs_table[type].cbs[i]->apply) + housekeeping_cbs_table[type].cbs[i]->apply(type); + } +} + bool housekeeping_enabled(enum hk_type type) { return !!(READ_ONCE(housekeeping.flags) & BIT(type)); -- 2.43.0

