From: Ziggy471 Date: Mon, 13 Dec 2010 17:11:30 +0000 (-0500) Subject: Automatic process group scheduling X-Git-Url: https://ziggy471.com/git/gitweb.cgi?p=ziggy471-frankenstein-kernel.git;a=commitdiff;h=9b76b856ed2d78b88bd5a8b78350fe774d2fc836 Automatic process group scheduling --- --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1618,6 +1618,8 @@ and is between 256 and 4096 characters. noapic [SMP,APIC] Tells the kernel to not make use of any IOAPICs that may be present in the system. + noautogroup Disable scheduler automatic task group creation. + nobats [PPC] Do not use BATs for mapping kernel lowmem on "Classic" PPC cores. --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -3029,6 +3029,8 @@ static void __proc_set_tty(struct task_s put_pid(tsk->signal->tty_old_pgrp); tsk->signal->tty = tty_kref_get(tty); tsk->signal->tty_old_pgrp = NULL; + + sched_autogroup_create_attach(tsk); } static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -552,6 +552,8 @@ struct thread_group_cputimer { spinlock_t lock; }; +struct autogroup; + /* * NOTE! "signal_struct" does not have it's own * locking, because a shared signal_struct always @@ -618,6 +620,10 @@ struct signal_struct { struct tty_struct *tty; /* NULL if no tty */ +#ifdef CONFIG_SCHED_AUTOGROUP + struct autogroup *autogroup; +#endif + /* * Cumulative resource counters for dead threads in the group, * and for reaped dead child processes forked by this group. @@ -1944,6 +1950,19 @@ int sched_rt_handler(struct ctl_table *t extern unsigned int sysctl_sched_compat_yield; +#ifdef CONFIG_SCHED_AUTOGROUP +extern unsigned int sysctl_sched_autogroup_enabled; +extern void sched_autogroup_create_attach(struct task_struct *p); +extern void sched_autogroup_detach(struct task_struct *p); +extern void sched_autogroup_fork(struct signal_struct *sig); +extern void sched_autogroup_exit(struct signal_struct *sig); +#else +static inline void sched_autogroup_create_attach(struct task_struct *p) { } +static inline void sched_autogroup_detach(struct task_struct *p) { } +static inline void sched_autogroup_fork(struct signal_struct *sig) { } +static inline void sched_autogroup_exit(struct signal_struct *sig) { } +#endif + #ifdef CONFIG_RT_MUTEXES extern int rt_mutex_getprio(struct task_struct *p); extern void rt_mutex_setprio(struct task_struct *p, int prio); --- a/init/Kconfig +++ b/init/Kconfig @@ -599,6 +599,18 @@ config CGROUP_MEM_RES_CTLR_SWAP endif # CGROUPS +config SCHED_AUTOGROUP + bool "Automatic process group scheduling" + select CGROUPS + select CGROUP_SCHED + select FAIR_GROUP_SCHED + help + This option optimizes the scheduler for common desktop workloads by + automatically creating and populating task groups. This separation + of workloads isolates aggressive CPU burners (like build jobs) from + desktop applications. Task group autogeneration is currently based + upon task tty association. + config MM_OWNER bool --- a/kernel/fork.c +++ b/kernel/fork.c @@ -923,6 +923,8 @@ static int copy_signal(unsigned long clo acct_init_pacct(&sig->pacct); tty_audit_fork(sig); + + sched_autogroup_fork(sig); sig->oom_adj = current->signal->oom_adj; --- a/kernel/sched.c +++ b/kernel/sched.c @@ -76,6 +76,7 @@ #include #include "sched_cpupri.h" +#include "sched_autogroup.h" #define CREATE_TRACE_POINTS #include @@ -346,19 +347,29 @@ struct task_group init_task_group; /* return group to which a task belongs */ static inline struct task_group *task_group(struct task_struct *p) { +#ifdef CONFIG_USER_SCHED struct task_group *tg; -#ifdef CONFIG_USER_SCHED rcu_read_lock(); tg = __task_cred(p)->user->tg; rcu_read_unlock(); + + return tg; #elif defined(CONFIG_CGROUP_SCHED) - tg = container_of(task_subsys_state(p, cpu_cgroup_subsys_id), - struct task_group, css); + struct task_group *tg; + struct cgroup_subsys_state *css; + + css = task_subsys_state(p, cpu_cgroup_subsys_id); + tg = container_of(css, struct task_group, css); + + return autogroup_task_group(p, tg); #else + struct task_group *tg; + tg = &init_task_group; -#endif + return tg; +#endif } /* Change a task's cfs_rq and parent entity if it moves across CPUs/groups */ @@ -1849,6 +1860,7 @@ static inline void __set_task_cpu(struct #include "sched_idletask.c" #include "sched_fair.c" #include "sched_rt.c" +#include "sched_autogroup.c" #ifdef CONFIG_SCHED_DEBUG # include "sched_debug.c" #endif @@ -9599,6 +9611,8 @@ void __init sched_init(void) init_task_group.parent = &root_task_group; list_add(&init_task_group.siblings, &root_task_group.children); #endif /* CONFIG_USER_SCHED */ + + autogroup_init(&init_task); #endif /* CONFIG_GROUP_SCHED */ #if defined CONFIG_FAIR_GROUP_SCHED && defined CONFIG_SMP @@ -10149,15 +10163,11 @@ void sched_destroy_group(struct task_gro /* change task's runqueue when it moves between groups. * The caller of this function should have put the task in its new group * by now. This function just updates tsk->se.cfs_rq and tsk->se.parent to - * reflect its new group. + * reflect its new group. Called with the runqueue lock held. */ -void sched_move_task(struct task_struct *tsk) +void __sched_move_task(struct task_struct *tsk, struct rq *rq) { int on_rq, running; - unsigned long flags; - struct rq *rq; - - rq = task_rq_lock(tsk, &flags); update_rq_clock(rq); @@ -10180,6 +10190,15 @@ void sched_move_task(struct task_struct tsk->sched_class->set_curr_task(rq); if (on_rq) enqueue_task(rq, tsk, 0, false); +} + +void sched_move_task(struct task_struct *tsk) +{ + struct rq *rq; + unsigned long flags; + + rq = task_rq_lock(tsk, &flags); + __sched_move_task(tsk, rq); task_rq_unlock(rq, &flags); } --- /dev/null +++ b/kernel/sched_autogroup.c @@ -0,0 +1,140 @@ +#ifdef CONFIG_SCHED_AUTOGROUP + +unsigned int __read_mostly sysctl_sched_autogroup_enabled = 1; + +struct autogroup { + struct kref kref; + struct task_group *tg; +}; + +static struct autogroup autogroup_default; + +static void autogroup_init(struct task_struct *init_task) +{ + autogroup_default.tg = &init_task_group; + kref_init(&autogroup_default.kref); + init_task->signal->autogroup = &autogroup_default; +} + +static inline void autogroup_destroy(struct kref *kref) +{ + struct autogroup *ag = container_of(kref, struct autogroup, kref); + struct task_group *tg = ag->tg; + + kfree(ag); + sched_destroy_group(tg); +} + +static inline void autogroup_kref_put(struct autogroup *ag) +{ + kref_put(&ag->kref, autogroup_destroy); +} + +static inline struct autogroup *autogroup_kref_get(struct autogroup *ag) +{ + kref_get(&ag->kref); + return ag; +} + +static inline struct autogroup *autogroup_create(void) +{ + struct autogroup *ag = kmalloc(sizeof(*ag), GFP_KERNEL); + + if (!ag) + goto out_fail; + + ag->tg = sched_create_group(&init_task_group); + kref_init(&ag->kref); + + if (!(IS_ERR(ag->tg))) + return ag; + +out_fail: + if (ag) { + kfree(ag); + WARN_ON(1); + } else + WARN_ON(1); + + return autogroup_kref_get(&autogroup_default); +} + +static inline struct task_group * +autogroup_task_group(struct task_struct *p, struct task_group *tg) +{ + int enabled = ACCESS_ONCE(sysctl_sched_autogroup_enabled); + + enabled &= (tg == &root_task_group); + enabled &= (p->sched_class == &fair_sched_class); + enabled &= (!(p->flags & PF_EXITING)); + + if (enabled) + return p->signal->autogroup->tg; + + return tg; +} + +static void +autogroup_move_group(struct task_struct *p, struct autogroup *ag) +{ + struct autogroup *prev; + struct task_struct *t; + struct rq *rq; + unsigned long flags; + + rq = task_rq_lock(p, &flags); + prev = p->signal->autogroup; + if (prev == ag) { + task_rq_unlock(rq, &flags); + return; + } + + p->signal->autogroup = autogroup_kref_get(ag); + __sched_move_task(p, rq); + task_rq_unlock(rq, &flags); + + rcu_read_lock(); + list_for_each_entry_rcu(t, &p->thread_group, thread_group) { + sched_move_task(t); + } + rcu_read_unlock(); + + autogroup_kref_put(prev); +} + +void sched_autogroup_create_attach(struct task_struct *p) +{ + struct autogroup *ag = autogroup_create(); + + autogroup_move_group(p, ag); + /* drop extra refrence added by autogroup_create() */ + autogroup_kref_put(ag); +} +EXPORT_SYMBOL(sched_autogroup_create_attach); + +/* currently has no users */ +void sched_autogroup_detach(struct task_struct *p) +{ + autogroup_move_group(p, &autogroup_default); +} +EXPORT_SYMBOL(sched_autogroup_detach); + +void sched_autogroup_fork(struct signal_struct *sig) +{ + sig->autogroup = autogroup_kref_get(current->signal->autogroup); +} + +void sched_autogroup_exit(struct signal_struct *sig) +{ + autogroup_kref_put(sig->autogroup); +} + +static int __init setup_autogroup(char *str) +{ + sysctl_sched_autogroup_enabled = 0; + + return 1; +} + +__setup("noautogroup", setup_autogroup); +#endif --- /dev/null +++ b/kernel/sched_autogroup.h @@ -0,0 +1,18 @@ +#ifdef CONFIG_SCHED_AUTOGROUP + +static void __sched_move_task(struct task_struct *tsk, struct rq *rq); + +static inline struct task_group * +autogroup_task_group(struct task_struct *p, struct task_group *tg); + +#else /* !CONFIG_SCHED_AUTOGROUP */ + +static inline void autogroup_init(struct task_struct *init_task) { } + +static inline struct task_group * +autogroup_task_group(struct task_struct *p, struct task_group *tg) +{ + return tg; +} + +#endif /* CONFIG_SCHED_AUTOGROUP */ --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -380,6 +380,18 @@ static struct ctl_table kern_table[] = { .mode = 0644, .proc_handler = &proc_dointvec, }, +#ifdef CONFIG_SCHED_AUTOGROUP + { + .ctl_name = CTL_UNNUMBERED, + .procname = "sched_autogroup_enabled", + .data = &sysctl_sched_autogroup_enabled, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_dointvec, + .extra1 = &zero, + .extra2 = &one, + }, +#endif #ifdef CONFIG_PROVE_LOCKING { .ctl_name = CTL_UNNUMBERED,