Currently, most housekeeping types (timer, rcu, misc, etc.) share the same mask (HK_TYPE_KERNEL_NOISE), preventing fine-grained control over CPU isolation. Additionally, there is no way to update these masks at runtime.
Separate the housekeeping types into individual enum entries and expand the cpumasks array to accommodate them. Introduce a sysfs interface at /sys/kernel/housekeeping/ that allows users to view and update the housekeeping masks for each type at runtime. When a mask is updated via sysfs, a notifier is triggered to re-affine the relevant kernel subsystems and services. Signed-off-by: Qiliang Yuan <[email protected]> Signed-off-by: Qiliang Yuan <[email protected]> --- include/linux/sched/isolation.h | 20 +++--- kernel/sched/isolation.c | 134 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 140 insertions(+), 14 deletions(-) diff --git a/include/linux/sched/isolation.h b/include/linux/sched/isolation.h index 5c07e3e9b8d1..e8fe1cfa5022 100644 --- a/include/linux/sched/isolation.h +++ b/include/linux/sched/isolation.h @@ -10,21 +10,17 @@ enum hk_type { HK_TYPE_DOMAIN, HK_TYPE_MANAGED_IRQ, - HK_TYPE_KERNEL_NOISE, + HK_TYPE_TICK, + HK_TYPE_TIMER, + HK_TYPE_RCU, + HK_TYPE_MISC, + HK_TYPE_WQ, + HK_TYPE_KTHREAD, HK_TYPE_MAX, - - /* - * The following housekeeping types are only set by the nohz_full - * boot commandline option. So they can share the same value. - */ - HK_TYPE_TICK = HK_TYPE_KERNEL_NOISE, - HK_TYPE_TIMER = HK_TYPE_KERNEL_NOISE, - HK_TYPE_RCU = HK_TYPE_KERNEL_NOISE, - HK_TYPE_MISC = HK_TYPE_KERNEL_NOISE, - HK_TYPE_WQ = HK_TYPE_KERNEL_NOISE, - HK_TYPE_KTHREAD = HK_TYPE_KERNEL_NOISE }; +#define HK_TYPE_KERNEL_NOISE HK_TYPE_TICK + struct housekeeping_update { enum hk_type type; const struct cpumask *new_mask; diff --git a/kernel/sched/isolation.c b/kernel/sched/isolation.c index 97cc41626a33..d9ce26cf31fe 100644 --- a/kernel/sched/isolation.c +++ b/kernel/sched/isolation.c @@ -9,14 +9,26 @@ */ #include <linux/sched/isolation.h> #include <linux/mutex.h> +#include <linux/kobject.h> +#include <linux/sysfs.h> +#include <linux/slab.h> +#include <linux/ctype.h> #include "sched.h" enum hk_flags { HK_FLAG_DOMAIN = BIT(HK_TYPE_DOMAIN), HK_FLAG_MANAGED_IRQ = BIT(HK_TYPE_MANAGED_IRQ), - HK_FLAG_KERNEL_NOISE = BIT(HK_TYPE_KERNEL_NOISE), + HK_FLAG_TICK = BIT(HK_TYPE_TICK), + HK_FLAG_TIMER = BIT(HK_TYPE_TIMER), + HK_FLAG_RCU = BIT(HK_TYPE_RCU), + HK_FLAG_MISC = BIT(HK_TYPE_MISC), + HK_FLAG_WQ = BIT(HK_TYPE_WQ), + HK_FLAG_KTHREAD = BIT(HK_TYPE_KTHREAD), }; +#define HK_FLAG_KERNEL_NOISE (HK_FLAG_TICK | HK_FLAG_TIMER | HK_FLAG_RCU | \ + HK_FLAG_MISC | HK_FLAG_WQ | HK_FLAG_KTHREAD) + static DEFINE_MUTEX(housekeeping_mutex); static BLOCKING_NOTIFIER_HEAD(housekeeping_notifier_list); DEFINE_STATIC_KEY_FALSE(housekeeping_overridden); @@ -99,7 +111,7 @@ int housekeeping_unregister_notifier(struct notifier_block *nb) } EXPORT_SYMBOL_GPL(housekeeping_unregister_notifier); -static int __maybe_unused housekeeping_update_notify(enum hk_type type, const struct cpumask *new_mask) +static int housekeeping_update_notify(enum hk_type type, const struct cpumask *new_mask) { struct housekeeping_update update = { .type = type, @@ -109,6 +121,124 @@ static int __maybe_unused housekeeping_update_notify(enum hk_type type, const st return blocking_notifier_call_chain(&housekeeping_notifier_list, HK_UPDATE_MASK, &update); } +static const char * const hk_type_names[] = { + [HK_TYPE_TIMER] = "timer", + [HK_TYPE_RCU] = "rcu", + [HK_TYPE_MISC] = "misc", + [HK_TYPE_TICK] = "tick", + [HK_TYPE_DOMAIN] = "domain", + [HK_TYPE_WQ] = "workqueue", + [HK_TYPE_MANAGED_IRQ] = "managed_irq", + [HK_TYPE_KTHREAD] = "kthread", +}; + +struct hk_attribute { + struct kobj_attribute kattr; + enum hk_type type; +}; + +#define to_hk_attr(_kattr) container_of(_kattr, struct hk_attribute, kattr) + +static ssize_t housekeeping_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct hk_attribute *hk_attr = to_hk_attr(attr); + const struct cpumask *mask = housekeeping_cpumask(hk_attr->type); + + return cpumap_print_to_pagebuf(false, buf, mask); +} + +static ssize_t housekeeping_store(struct kobject *kobject, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct hk_attribute *hk_attr = to_hk_attr(attr); + enum hk_type type = hk_attr->type; + cpumask_var_t new_mask; + int err; + + if (!alloc_cpumask_var(&new_mask, GFP_KERNEL)) + return -ENOMEM; + + err = cpulist_parse(buf, new_mask); + if (err) + goto out_free; + + if (cpumask_empty(new_mask)) { + err = -EINVAL; + goto out_free; + } + + mutex_lock(&housekeeping_mutex); + + if (!housekeeping.cpumasks[type]) { + if (!alloc_cpumask_var(&housekeeping.cpumasks[type], GFP_KERNEL)) { + err = -ENOMEM; + goto out_unlock; + } + } + + if (cpumask_equal(housekeeping.cpumasks[type], new_mask)) { + err = 0; + goto out_unlock; + } + + cpumask_copy(housekeeping.cpumasks[type], new_mask); + housekeeping.flags |= BIT(type); + static_branch_enable(&housekeeping_overridden); + + housekeeping_update_notify(type, new_mask); + + err = count; + +out_unlock: + mutex_unlock(&housekeeping_mutex); +out_free: + free_cpumask_var(new_mask); + return err < 0 ? err : count; +} + +static struct hk_attribute housekeeping_attrs[HK_TYPE_MAX]; +static struct attribute *housekeeping_attr_ptr[HK_TYPE_MAX + 1]; + +static const struct attribute_group housekeeping_attr_group = { + .attrs = housekeeping_attr_ptr, +}; + +static int __init housekeeping_sysfs_init(void) +{ + struct kobject *housekeeping_kobj; + int i, j = 0; + int ret; + + housekeeping_kobj = kobject_create_and_add("housekeeping", kernel_kobj); + if (!housekeeping_kobj) + return -ENOMEM; + + for (i = 0; i < HK_TYPE_MAX; i++) { + if (!hk_type_names[i]) + continue; + + housekeeping_attrs[i].type = i; + sysfs_attr_init(&housekeeping_attrs[i].kattr.attr); + housekeeping_attrs[i].kattr.attr.name = hk_type_names[i]; + housekeeping_attrs[i].kattr.attr.mode = 0644; + housekeeping_attrs[i].kattr.show = housekeeping_show; + housekeeping_attrs[i].kattr.store = housekeeping_store; + housekeeping_attr_ptr[j++] = &housekeeping_attrs[i].kattr.attr; + } + housekeeping_attr_ptr[j] = NULL; + + ret = sysfs_create_group(housekeeping_kobj, &housekeeping_attr_group); + if (ret) { + kobject_put(housekeeping_kobj); + return ret; + } + + return 0; +} +late_initcall(housekeeping_sysfs_init); + void housekeeping_init(void) { enum hk_type type; -- 2.51.0

