Skip to contentSkip to conversation

sched: automated per tty task groups

PATCH Superseded Mike Galbraith 41 participants 264 messages Oct 19, 2010 – Jan 4, 2011 2 messages not in the archive Page 1 of 2

Subsystems (8)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 sched: automated per tty task groups

Greetings,

Comments, suggestions etc highly welcome.

This patch implements an idea from Linus, to automatically create task groups per tty, to improve desktop interactivity under hefty load such as kbuild. The feature is enabled from boot by default, The default setting can be changed via the boot option ttysched=0, and can be can be turned on or off on the fly via echo [01] > /proc/sys/kernel/sched_tty_sched_enabled.

A 100% hog overhead measurement proggy pinned to the same CPU as a make -j10

pert/s:      229 >5484.43us:       41 min:  0.15 max:12069.42 avg:2193.81 sum/s:502382us overhead:50.24%
pert/s:      222 >5652.28us:       43 min:  0.46 max:12077.31 avg:2248.56 sum/s:499181us overhead:49.92%
pert/s:      211 >5809.38us:       43 min:  0.16 max:12064.78 avg:2381.70 sum/s:502538us overhead:50.25%
pert/s:      223 >6147.92us:       43 min:  0.15 max:16107.46 avg:2282.17 sum/s:508925us overhead:50.49%
pert/s:      218 >6252.64us:       43 min:  0.16 max:12066.13 avg:2324.11 sum/s:506656us overhead:50.27%
---
 drivers/char/tty_io.c |    2 
 include/linux/sched.h |   14 +++++
 include/linux/tty.h   |    3 +
 init/Kconfig          |   13 +++++
 kernel/sched.c        |    9 +++
 kernel/sched_tty.c    |  128 ++++++++++++++++++++++++++++++++++++++++++++++++++
 kernel/sched_tty.h    |    7 ++
 kernel/sysctl.c       |   11 ++++
 8 files changed, 186 insertions(+), 1 deletion(-)
include/linux/sched.h +14 -0
Index: linux-2.6.36.git/include/linux/sched.h
===================================================================
@@ -1900,6 +1900,20 @@ int sched_rt_handler(struct ctl_table *t
 
 extern unsigned int sysctl_sched_compat_yield;
 
+#ifdef CONFIG_SCHED_DESKTOP
+int sched_tty_sched_handler(struct ctl_table *table, int write,
+		void __user *buffer, size_t *lenp,
+		loff_t *ppos);
+
+extern unsigned int sysctl_sched_tty_sched_enabled;
+
+void tty_sched_create_group(struct tty_struct *tty);
+void tty_sched_destroy_group(struct tty_struct *tty);
+#else
+static inline void tty_sched_create_group(struct tty_struct *tty) { }
+static inline void tty_sched_destroy_group(struct tty_struct *tty) { }
+#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);
include/linux/tty.h +3 -0
Index: linux-2.6.36.git/include/linux/tty.h
===================================================================
@@ -327,6 +327,9 @@ struct tty_struct {
 	/* If the tty has a pending do_SAK, queue it here - akpm */
 	struct work_struct SAK_work;
 	struct tty_port *port;
+#ifdef CONFIG_SCHED_DESKTOP
+	struct task_group *tg;
+#endif
 };
 
 /* Each of a tty's open files has private_data pointing to tty_file_private */
kernel/sched.c +8 -1
Index: linux-2.6.36.git/kernel/sched.c
===================================================================
@@ -78,6 +78,7 @@
 
 #include "sched_cpupri.h"
 #include "workqueue_sched.h"
+#include "sched_tty.h"
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/sched.h>
@@ -612,11 +613,16 @@ static inline int cpu_of(struct rq *rq)
  */
 static inline struct task_group *task_group(struct task_struct *p)
 {
+	struct task_group *tg;
 	struct cgroup_subsys_state *css;
 
 	css = task_subsys_state_check(p, cpu_cgroup_subsys_id,
 			lockdep_is_held(&task_rq(p)->lock));
-	return container_of(css, struct task_group, css);
+	tg = container_of(css, struct task_group, css);
+
+	tty_sched_check_attach(p, &tg);
+
+	return tg;
 }
 
 /* Change a task's cfs_rq and parent entity if it moves across CPUs/groups */
@@ -1920,6 +1926,7 @@ static void deactivate_task(struct rq *r
 #include "sched_idletask.c"
 #include "sched_fair.c"
 #include "sched_rt.c"
+#include "sched_tty.c"
 #ifdef CONFIG_SCHED_DEBUG
 # include "sched_debug.c"
 #endif
drivers/char/tty_io.c +2 -0
Index: linux-2.6.36.git/drivers/char/tty_io.c
===================================================================
@@ -185,6 +185,7 @@ void free_tty_struct(struct tty_struct *
 {
 	kfree(tty->write_buf);
 	tty_buffer_free_all(tty);
+	tty_sched_destroy_group(tty);
 	kfree(tty);
 }
 
@@ -2823,6 +2824,7 @@ void initialize_tty_struct(struct tty_st
 	tty->ops = driver->ops;
 	tty->index = idx;
 	tty_line_name(driver, idx, tty->name);
+	tty_sched_create_group(tty);
 }
 
 /**
kernel/sched_tty.h new file +7 -0
Index: linux-2.6.36.git/kernel/sched_tty.h
===================================================================
@@ -0,0 +1,7 @@
+#ifdef CONFIG_SCHED_DESKTOP
+static inline void
+tty_sched_check_attach(struct task_struct *p, struct task_group **tg);
+#else
+static inline void
+tty_sched_check_attach(struct task_struct *p, struct task_group **tg) { }
+#endif
kernel/sched_tty.c new file +128 -0

128 lines not on this page · raw message

kernel/sysctl.c +11 -0
Index: linux-2.6.36.git/kernel/sysctl.c
===================================================================
@@ -384,6 +384,17 @@ static struct ctl_table kern_table[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec,
 	},
+#ifdef CONFIG_SCHED_DESKTOP
+	{
+		.procname	= "sched_tty_sched_enabled",
+		.data		= &sysctl_sched_tty_sched_enabled,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= sched_tty_sched_handler,
+		.extra1		= &zero,
+		.extra2		= &one,
+	},
+#endif
 #ifdef CONFIG_PROVE_LOCKING
 	{
 		.procname	= "prove_locking",
init/Kconfig +13 -0
Index: linux-2.6.36.git/init/Kconfig
===================================================================
@@ -652,6 +652,19 @@ config DEBUG_BLK_CGROUP
 
 endif # CGROUPS
 
+config SCHED_DESKTOP
+	bool "Desktop centric group scheduling"
+	depends on EXPERIMENTAL
+	select CGROUPS
+	select CGROUP_SCHED
+	select FAIR_GROUP_SCHED
+	select RT_GROUP_SCHED
+	select BLK_CGROUP
+	help
+	  This option optimizes the group scheduler for common desktop workloads,
+	  by creating separate per tty groups. This separation of workloads isolates
+	  aggressive CPU burners (like build jobs) from desktop applications.
+
 config MM_OWNER
 	bool
 
maintainer
#1 lore raw
Quoted 3 lines · Mike Galbraith

Greetings,

Comments, suggestions etc highly welcome.

You might be wanting to exclude RT tasks from the tty groups since there is no interface to grant them any runtime and such :-)

Also, I think tty_sched_move_task and sched_move_task() should be sharing lots more code -- I recently proposed a fix for sched_move_task() because the Android people complained, but they haven't replied back yet..

#2 lore raw
Quoted 7 lines · Peter Zijlstra
Quoted 3 lines · Mike Galbraith

Greetings,

Comments, suggestions etc highly welcome.

You might be wanting to exclude RT tasks from the tty groups since there is no interface to grant them any runtime and such :-)

:)

Quoted 4 lines

Also, I think tty_sched_move_task and sched_move_task() should be sharing lots more code -- I recently proposed a fix for sched_move_task() because the Android people complained, but they haven't replied back yet..

Yeah. I should be able to just do sched_move_task(), but it doesn't currently work even with your patch, turning tty_sched on/off can lead to incredible delays before you get box back. With virgin source, it's size infinity for all intents and purposes.

-Mike

maintainer
#3 lore raw
Quoted 5 lines · Mike Galbraith

Yeah. I should be able to just do sched_move_task(), but it doesn't currently work even with your patch, turning tty_sched on/off can lead to incredible delays before you get box back. With virgin source, it's size infinity for all intents and purposes.

Ah, first feedback I've got on that patch,. but surely since you created one that does work we can fix my patch and use the normal path? :-)

#4 lore raw
Quoted 9 lines · Peter Zijlstra
Quoted 5 lines · Mike Galbraith

Yeah. I should be able to just do sched_move_task(), but it doesn't currently work even with your patch, turning tty_sched on/off can lead to incredible delays before you get box back. With virgin source, it's size infinity for all intents and purposes.

Ah, first feedback I've got on that patch,. but surely since you created one that does work we can fix my patch and use the normal path? :-)

Yeah. I wanted to get the RFC out the door, so took a shortcut.

-Mike

#5 lore raw
In reply to Peter Zijlstra
Quoted 9 lines · Peter Zijlstra
Quoted 5 lines · Mike Galbraith

Yeah. I should be able to just do sched_move_task(), but it doesn't currently work even with your patch, turning tty_sched on/off can lead to incredible delays before you get box back. With virgin source, it's size infinity for all intents and purposes.

Ah, first feedback I've got on that patch,. but surely since you created one that does work we can fix my patch and use the normal path? :-)

Actually, your patch works just peachy, my fiddling with sleepers vruntime at attach time was a dainbramaged thing to do.

-Mike

maintainer
#6 lore raw
Quoted 2 lines · Mike Galbraith

Actually, your patch works just peachy, my fiddling with sleepers vruntime at attach time was a dainbramaged thing to do.

OK, I'll queue it with a Tested-by from you. Thanks!

maintainer
#7 lore raw
In reply to Mike Galbraith

kernel/sched_tty.c line 113

Quoted 13 lines · Mike Galbraith
kernel/sched_tty.c new file +13 -0
+       read_lock_irqsave(&tasklist_lock, flags);
+
+       rcu_read_lock();
+       for_each_process(p) {
+               tg = task_group(p);
+               tty_sched_move_task(p, tg);
+               list_for_each_entry_rcu(t, &p->thread_group, thread_group) {
+                       tty_sched_move_task(t, tg);
+               }
+       }
+       rcu_read_unlock();
+
+       read_unlock_irqrestore(&tasklist_lock, flags); 

I don't think you need to disable IRQs for tasklist lock, nor do I think you actually need it.

If you enable tty groups and then scan all the existing tasks you've covered them all, new tasks will already be placed right, dying tasks we don't care about anyway.

#8 lore raw
Quoted 17 lines · Peter Zijlstra

kernel/sched_tty.c line 113

Quoted 13 lines · Mike Galbraith
kernel/sched_tty.c new file +13 -0
+       read_lock_irqsave(&tasklist_lock, flags);
+
+       rcu_read_lock();
+       for_each_process(p) {
+               tg = task_group(p);
+               tty_sched_move_task(p, tg);
+               list_for_each_entry_rcu(t, &p->thread_group, thread_group) {
+                       tty_sched_move_task(t, tg);
+               }
+       }
+       rcu_read_unlock();
+
+       read_unlock_irqrestore(&tasklist_lock, flags); 

I don't think you need to disable IRQs for tasklist lock, nor do I think you actually need it.

OK, thanks. (No such thing as too paranoid;)

-Mike

#9 lore raw
In reply to Mike Galbraith

It was suggested that I show a bit more info.

Quoted 7 lines · Mike Galbraith

A 100% hog overhead measurement proggy pinned to the same CPU as a make -j10

pert/s:      229 >5484.43us:       41 min:  0.15 max:12069.42 avg:2193.81 sum/s:502382us overhead:50.24%
pert/s:      222 >5652.28us:       43 min:  0.46 max:12077.31 avg:2248.56 sum/s:499181us overhead:49.92%
pert/s:      211 >5809.38us:       43 min:  0.16 max:12064.78 avg:2381.70 sum/s:502538us overhead:50.25%
pert/s:      223 >6147.92us:       43 min:  0.15 max:16107.46 avg:2282.17 sum/s:508925us overhead:50.49%
pert/s:      218 >6252.64us:       43 min:  0.16 max:12066.13 avg:2324.11 sum/s:506656us overhead:50.27%

The same load without per tty task groups.

pert/s:       31 >40475.37us:        3 min:  0.37 max:48103.60 avg:29573.74 sum/s:916786us overhead:90.24%
pert/s:       23 >41237.70us:       12 min:  0.36 max:56010.39 avg:40187.01 sum/s:924301us overhead:91.99%
pert/s:       24 >42150.22us:       12 min:  8.86 max:61265.91 avg:39459.91 sum/s:947038us overhead:92.20%
pert/s:       26 >42344.91us:       11 min:  3.83 max:52029.60 avg:36164.70 sum/s:940282us overhead:91.12%
pert/s:       24 >44262.90us:       14 min:  5.05 max:82735.15 avg:40314.33 sum/s:967544us overhead:92.22%

^^^^^usecs ^^^^^usecs ^^the competition got

Average service latency is an order of magnitude better with tty_sched. (Imagine that pert is Xorg or whatnot instead)

Using Mathieu Desnoyers' wakeup-latency testcase (attached):

With taskset -c 3 make -j 10 running..

taskset -c 3 ./wakeup-latency& sleep 30;killall wakeup-latency

without:
maximum latency: 42963.2 µs
average latency: 9077.0 µs
missed timer events: 0

with:
maximum latency: 4160.7 µs
average latency: 149.4 µs
missed timer events: 0

Patch makes a big difference in desktop feel under hefty load here.

-Mike

/*

Test application to test wakeup latency under different kinds of conditions and scheduling systems.

Copyright (C) 2008 Nokia Corporation. Copyright (C) 2008 Jussi Laako <[email protected]>

Contact: Jussi Laako <[email protected]>
Mathieu Desnoyers <[email protected]>

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

Compile with:
gcc -lm -lrt -o wakeup-latency wakeup-latency.c
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <math.h>
#include <sched.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define WL_CLOCK_ID CLOCK_MONOTONIC
/*#define WL_CLOCK_ID CLOCK_REALTIME*/
#define WL_INTERVAL 10 /* in milliseconds */
#define WL_DELTA 0.01 /* same as above, in seconds */
#define WL_REPORT 0.005 /* report threshold */
#define WL_BLOCKSIZE 480
#define WL_WORKCOUNT 2

typedef struct _ctx_t

{
	timer_t tid;
	unsigned count;
	unsigned overruns;
	double prevtime;
	double maxlat;
	double avglat;
	int dowork;
} ctx_t;

static int workcount = WL_WORKCOUNT;
static float x[4 + 1] = { 0 };
static float y[4] = { 0 };
static float wrk[WL_BLOCKSIZE];

static int tracefd;

static void usertrace(char *str)

{
	write(tracefd, str, strlen(str));
}

static inline double time_as_double (void)

{
	double restime;
	struct timespec ts = { 0 };  /* zero result in case of failure */

	clock_gettime(WL_CLOCK_ID, &ts);

	restime = (double) ts.tv_sec;
	restime += (double) ts.tv_nsec * 1.0e-9;

	return restime;
}

static void flt (float *data, int count)

{
	int i;
	float by;
	float ay;
	float yn;
	static const float a[] = {
		1.0F,
		2.000399874F,
		1.777661733F,
		0.7472161963F,
		0.1247940929F
	};
	static const float b[] = {
		0.3531294936F,
		1.412517974F,
		2.118776961F,
		1.412517974F,
		0.3531294936F
	};

	while (count)
	{
		for (i = 1; i <= 4; i++)
			x[i - 1] = x[i];
		x[4] = *data;
		by = 0;
		for (i = 0; i <= 4; i++)
			by += b[i] * x[4 - i];
		ay = 0;
		for (i = 1; i <= 4; i++)
			ay += a[i] * y[4 - i];
		yn = by - ay;
		for (i = 1; i < 4; i++)
			y[i - 1] = y[i];
		y[4 - 1] = yn;
		*data++ = yn;
		count--;
	}
}

static void sig_cb (int signo)

{
	/* no-op */
}

static void event_cb (sigval_t sval)

{
	int i, w;
	int or;
	int rnd;
	float s;
	float rms;
	double curtime;
	double delta;
	double lat;
	ctx_t *lctx = (ctx_t *) sval.sival_ptr;
	char buf[256];

	curtime = time_as_double();
	or = timer_getoverrun(lctx->tid);
	if (or > 0)
	{
		snprintf(buf, 256, "overruns: %i", or);
		usertrace(buf);
		printf("%s\n", buf);
		lctx->overruns += or;
	}
	delta = curtime - lctx->prevtime;
	if (delta > WL_DELTA)
	{
		lat = delta - WL_DELTA;
		/* display only the ones exceeding the threshold */
		if (lat > WL_REPORT) {
			snprintf(buf, 256, "late by: %.1f µs", lat * 1.0e6);
			usertrace(buf);
			printf("%s\n", buf);
		}
		if (lat > lctx->maxlat)
			lctx->maxlat = lat;
		lctx->avglat += lat;
		lctx->count++;
	}
	lctx->prevtime = curtime;
	if (lctx->dowork)
	{
		for (w = 0; w < workcount; w++)
		{
			s = 1.0F / (float) (RAND_MAX / 2);
			for (i = 0; i < WL_BLOCKSIZE; i++)
			{
				rnd = rand() - (RAND_MAX >> 1);
				wrk[i] = (float) rnd * s;
			}
			flt(wrk, WL_BLOCKSIZE);
			rms = 0.0F;
			for (i = 0; i < WL_BLOCKSIZE; i++)
				rms += wrk[i];
			rms = sqrtf(rms / (float) WL_BLOCKSIZE);
		}
	}
}

static int set_scheduling (int schedpol)

{
	struct sched_param schedparm = { 0 };

	printf("min priority: %i, max priority: %i\n",
		sched_get_priority_min(schedpol),
		sched_get_priority_max(schedpol));
	if (schedpol == SCHED_FIFO || schedpol == SCHED_RR)
		schedparm.sched_priority = sched_get_priority_min(schedpol);
	else
		schedparm.sched_priority = sched_get_priority_max(schedpol);
	if (sched_setscheduler(0, schedpol, &schedparm))
	{
		perror("sched_setscheduler()");
		return -1;
	}
	return 0;
}

static int create_source (ctx_t *ctx, int schedpol)

{
	int res = 0;
	struct sigevent sev = { 0 };
	struct sched_param schedparm = { 0 };
	pthread_attr_t tattr;

	pthread_attr_init(&tattr);
	if (pthread_attr_setinheritsched(&tattr, PTHREAD_INHERIT_SCHED))
	{
		pthread_attr_setinheritsched(&tattr, PTHREAD_EXPLICIT_SCHED);
		pthread_attr_setschedpolicy(&tattr, schedpol);
		if (schedpol == SCHED_FIFO || schedpol == SCHED_RR)
			schedparm.sched_priority =
				sched_get_priority_min(schedpol);
		else
			schedparm.sched_priority =
				sched_get_priority_max(schedpol);
		pthread_attr_setschedparam(&tattr, &schedparm);
	}

	sev.sigev_notify = SIGEV_THREAD;
	sev.sigev_signo = SIGRTMIN;
	sev.sigev_value.sival_ptr = ctx;
	sev.sigev_notify_function = event_cb;
	sev.sigev_notify_attributes = &tattr;
	if (timer_create(WL_CLOCK_ID, &sev, &ctx->tid))
	{
		perror("timer_create()");
		res = -1;
	}

	pthread_attr_destroy(&tattr);

	return res;
}

static int set_timer (ctx_t *ctx)

{
	struct itimerspec ts;

	ts.it_interval.tv_sec = 0;
	ts.it_interval.tv_nsec = WL_INTERVAL * 1000000;
	ts.it_value.tv_sec = 0;
	ts.it_value.tv_nsec = WL_INTERVAL * 1000000;
	ctx->prevtime = time_as_double();
	if (timer_settime(ctx->tid, 0, &ts, NULL))
	{
		perror("timer_settime()");
		return -1;
	}
	return 0;
}

int main (int argc, char *argv[])

{
	int i;
	int w;
	int schedpol = 0;
	int sig;
	int ret = 0;
	sigset_t ss;
	ctx_t ctx = { 0 };

	tracefd = open("/debugfs/ltt/write_event", O_WRONLY);

	signal(SIGINT, sig_cb);
	signal(SIGTERM, sig_cb);
	srand(time(NULL));
	for (i = 1; i < argc; i++)
	{
		if (strcmp(argv[i], "--use-mm") == 0)
			schedpol = 6;
		else if (strcmp(argv[i], "--use-fifo") == 0)
			schedpol = SCHED_FIFO;
		else if (strcmp(argv[i], "--use-rr") == 0)
			schedpol = SCHED_RR;
		else if (strcmp(argv[i], "--do-work") == 0)
		{
			ctx.dowork = 1;
			if (i + 1 < argc)
			{
				w = atoi(argv[i + 1]);
				if (w > 0)
				{
					workcount = w;
					i++;
				}
			}
			printf("work count: %i\n", workcount);
		}
		else
		{
			printf("unknown option: %s\n", argv[i]);
			printf(
				"usage: %s [--use-mm|--use-fifo|--use-rr][--do-work [n]]\n",
				argv[0]);
			ret = 1;
			goto end;
		}
	}

	if (set_scheduling(schedpol)) {
		ret = 1;
		goto end;
	}

	if (create_source(&ctx, schedpol)) {
		ret = 1;
		goto end;
	}

	if (!set_timer(&ctx))
	{
		sigemptyset(&ss);
		sigaddset(&ss, SIGINT);
		sigaddset(&ss, SIGTERM);
		sigwait(&ss, &sig);
	}

	printf("\nmaximum latency: %.1f µs\n", ctx.maxlat * 1.0e6);
	printf("average latency: %.1f µs\n",
		ctx.avglat / (double) ctx.count * 1.0e6);
	printf("missed timer events: %u\n", ctx.overruns);

	timer_delete(ctx.tid);
end:
	close(tracefd);

	return ret;
}
#10 lore raw
Quoted 11 lines · Mike Galbraith

Using Mathieu Desnoyers' wakeup-latency testcase (attached):

With taskset -c 3 make -j 10 running..

taskset -c 3 ./wakeup-latency& sleep 30;killall wakeup-latency

without:
maximum latency: 42963.2 µs
average latency: 9077.0 µs
missed timer events: 0

Quoted 6 lines

with:
maximum latency: 4160.7 µs
average latency: 149.4 µs
missed timer events: 0

Patch makes a big difference in desktop feel under hefty load here.

That's really nice!

Could this feature realistically do block IO isolation as well? It's always annoying when some big IO job is making the desktop jerky. Especially as your patch is selecting the block cgroup feature already:

+ select BLK_CGROUP

Thanks,

Ingo

#11 lore raw
Quoted 5 lines · Ingo Molnar

Could this feature realistically do block IO isolation as well? It's always annoying when some big IO job is making the desktop jerky. Especially as your patch is selecting the block cgroup feature already:

+ select BLK_CGROUP

I know my cgroup pgid config helps a bunch with rummaging in my email while other IO is going on. I've been attributing that to BLK_CGROUP, but have no proof.

-Mike

#12 lore raw
In reply to Mike Galbraith
Quoted 1 line · Mike Galbraith

It was suggested that I show a bit more info.

Yes, I was going to complain that the numbers in the commit message made no sense without something to compare the numbers to.

Quoted 1 line

The same load without per tty task groups.

Very impressive. This definitely looks like something people will notice.

That said, I do think we should think carefully about calling this a "tty" feature. I think we might want to leave the door open to other heuristics than _just_ the tty group. I think the tty group approach is wonderful for traditional Unix loads in a desktop environment, but I suspect we might hit issues with IDE's etc too. I don't know if we can notice things like that automatically, but I think it's worth thinking about.

So I think the patch looks pretty good, and the numbers seem to look just stunningly so, but I'd like to name the feature more along the lines of "automatic process group scheduling" rather than about tty's per se.

And you actually did that for the Kconfig option, which makes me quite happy.

The one other thing I do wonder about is how noticeable the group scheduling overhead is. If people compare with a non-CGROUP_SCHED kernel, will a desktop-optimized kernel suddenly have horrible pipe latency due to much higher scheduling cost? Right now that whole feature is hidden by EXPERIMENTAL, I don't know how much it hurts, and I never timed it when I tried it out long ago..

Linus

#13 lore raw
Quoted 6 lines · Linus Torvalds

On Tue, Oct 19, 2010 at 4:29 AM, Mike Galbraith <[email protected]> wrote:

So I think the patch looks pretty good, and the numbers seem to look just stunningly so, but I'd like to name the feature more along the lines of "automatic process group scheduling" rather than about tty's per se.

Oh, absolutely, that's what it's all about really. What I'd _like_ is to get per process group scheduling working on the cheap..ish. Your idea of tty cgoups looked much simpler though, so I figured that would be a great place to start. It turned out to be much simpler than I thought it would be, which is encouraging, and it works well in testing (so far that is).

Quoted 1 line

And you actually did that for the Kconfig option, which makes me quite happy.

(Ingo's input.. spot on)

Quoted 2 lines

The one other thing I do wonder about is how noticeable the group scheduling overhead is.

Very noticeable, cgroups is far from free. It would make no sense for a performance freak to even think about it. I don't run cgroup enabled kernels usually, and generally strip to the bone because I favor throughput very very heavily, but when I look at the desktop under load, the cost/performance trade-off ~seems to work out.

Quoted 5 lines

If people compare with a non-CGROUP_SCHED
kernel, will a desktop-optimized kernel suddenly have horrible pipe latency due to much higher scheduling cost? Right now that whole feature is hidden by EXPERIMENTAL, I don't know how much it hurts, and I never timed it when I tried it out long ago..

The scheduling cost is quite high. But realistically, the cost of a distro kernel with full featured network stack is (much) higher. I seriously doubt the cost of cgroups would be noticed by the typical _desktop_ user. Overall latencies for any switchy microbenchmark will certainly be considerably higher with the feature enabled.

-Mike

#14 lore raw
Quoted 7 lines · Linus Torvalds
Quoted 6 lines

If people compare with a non-CGROUP_SCHED
kernel, will a desktop-optimized kernel suddenly have horrible pipe latency due to much higher scheduling cost? Right now that whole feature is hidden by EXPERIMENTAL, I don't know how much it hurts, and I never timed it when I tried it out long ago..

Q/D test of kernels w/wo, with same .config using pipe-test (pure sched) gives on my box ~590khz with tty_sched active, 620khz without cgroups acitve in same kernel/config without patch. last time I measured stripped down config (not long ago, but not yesterday either) gave max ctx rate ~690khz on this box.

(note: very Q, very D numbers, no variance testing, ballpark)

-Mike

#15 lore raw
Quoted 8 lines · Mike Galbraith
Quoted 7 lines · Linus Torvalds
Quoted 6 lines

If people compare with a non-CGROUP_SCHED
kernel, will a desktop-optimized kernel suddenly have horrible pipe latency due to much higher scheduling cost? Right now that whole feature is hidden by EXPERIMENTAL, I don't know how much it hurts, and I never timed it when I tried it out long ago..

Quoted 6 lines

Q/D test of kernels w/wo, with same .config using pipe-test (pure sched) gives on my box ~590khz with tty_sched active, 620khz without cgroups acitve in same kernel/config without patch. last time I measured stripped down config (not long ago, but not yesterday either) gave max ctx rate ~690khz on this box.

(note: very Q, very D numbers, no variance testing, ballpark)

That's 5% overhead in context switches. Definitely not in the 'horrible' category.

This would be a rather tempting item for 2.6.37 ... especially as it really mainly reuses existing group scheduling functionality, in a clever way.

Mind doing more of the tty->desktop renames/generalizations as Linus suggested, and resend the patch?

I'd also suggest to move it out of EXPERIMENTAL - we dont really do that for core kernel features as most distros enable CONFIG_EXPERIMENTAL so it's a rather meaningless distinction. Since the feature is default-n, people will get the old scheduler by default but can also choose this desktop-centric scheduling mode.

I'd even argue to make it default-y, because this patch clearly cures a form of kbuild cancer.

Thanks,

Ingo

#16 lore raw
Quoted 2 lines · Ingo Molnar

Mind doing more of the tty->desktop renames/generalizations as Linus suggested, and resend the patch?

Here she comes. Better/Worse?

Changes:
- tty->autogroup.
- only autogroup fair class tasks.
- removed dainbramaged sleeper vruntime twiddling.
- removed paranoid locking.
- removed noop detatch code.

Quoted 7 lines

I'd also suggest to move it out of EXPERIMENTAL - we dont really do that for core kernel features as most distros enable CONFIG_EXPERIMENTAL so it's a rather meaningless distinction. Since the feature is default-n, people will get the old scheduler by default but can also choose this desktop-centric scheduling mode.

I'd even argue to make it default-y, because this patch clearly cures a form of kbuild cancer.

You top dogs can make the default call.. it it's accepted that is ;-)

Marcus: care to try the below? Works fine for me (but so did first cut). It depends on the attached patch, and applied to virgin shiny new 2.6.36.

A recurring complaint from CFS users is that parallel kbuild has a negative impact on desktop interactivity. This patch implements an idea from Linus, to automatically create task groups. This patch only implements Linus' per tty task group suggestion, and only for fair class tasks, but leaves the way open for enhancement.

How it works: at tty alloc/dealloc time, a task group is created/destroyed, so there is always a task group active per tty. When we select a runqueue, if the task has a has a tty association, and no task group, attach it to a per tty autogroup on the fly.

The feature is enabled from boot by default if CONFIG_SCHED_AUTOGROUP is selected, but can be disabled via the boot option noautogroup, and can be also be turned on/off on the fly via..
echo [01] > /proc/sys/kernel/sched_autogroup_enabled.
..which will automatically move tasks to/from the root task group.

Some numbers.

A 100% hog overhead measurement proggy pinned to the same CPU as a make -j10

About measurement proggy:
pert/sec = perturbations/sec
min/max/avg = scheduler service latencies in usecs
sum/s = time accrued by the competition per sample period (1 sec here) overhead = %CPU received by the competition per sample period

pert/s:       31 >40475.37us:        3 min:  0.37 max:48103.60 avg:29573.74 sum/s:916786us overhead:90.24%
pert/s:       23 >41237.70us:       12 min:  0.36 max:56010.39 avg:40187.01 sum/s:924301us overhead:91.99%
pert/s:       24 >42150.22us:       12 min:  8.86 max:61265.91 avg:39459.91 sum/s:947038us overhead:92.20%
pert/s:       26 >42344.91us:       11 min:  3.83 max:52029.60 avg:36164.70 sum/s:940282us overhead:91.12%
pert/s:       24 >44262.90us:       14 min:  5.05 max:82735.15 avg:40314.33 sum/s:967544us overhead:92.22%

Same load with this patch applied.

pert/s:      229 >5484.43us:       41 min:  0.15 max:12069.42 avg:2193.81 sum/s:502382us overhead:50.24%
pert/s:      222 >5652.28us:       43 min:  0.46 max:12077.31 avg:2248.56 sum/s:499181us overhead:49.92%
pert/s:      211 >5809.38us:       43 min:  0.16 max:12064.78 avg:2381.70 sum/s:502538us overhead:50.25%
pert/s:      223 >6147.92us:       43 min:  0.15 max:16107.46 avg:2282.17 sum/s:508925us overhead:50.49%
pert/s:      218 >6252.64us:       43 min:  0.16 max:12066.13 avg:2324.11 sum/s:506656us overhead:50.27%

Average service latency is an order of magnitude better with autogroup. (Imagine that pert were Xorg or whatnot instead)

Using Mathieu Desnoyers' wakeup-latency testcase:

With taskset -c 3 make -j 10 running..

taskset -c 3 ./wakeup-latency& sleep 30;killall wakeup-latency

without:
maximum latency: 42963.2 µs
average latency: 9077.0 µs
missed timer events: 0

with:
maximum latency: 4160.7 µs
average latency: 149.4 µs
missed timer events: 0

---
 drivers/char/tty_io.c    |    2 +
 include/linux/sched.h    |   14 ++++++++
 include/linux/tty.h      |    3 +
 init/Kconfig             |   13 ++++++++
 kernel/sched.c           |    9 ++++-
 kernel/sched_autogroup.c |   76 +++++++++++++++++++++++++++++++++++++++++++++++
 kernel/sched_autogroup.h |    7 ++++
 kernel/sysctl.c          |   11 ++++++
 8 files changed, 134 insertions(+), 1 deletion(-)
include/linux/sched.h +14 -0
Index: linux-2.6.36.git/include/linux/sched.h
===================================================================
@@ -1900,6 +1900,20 @@ int sched_rt_handler(struct ctl_table *t
 
 extern unsigned int sysctl_sched_compat_yield;
 
+#ifdef CONFIG_SCHED_AUTOGROUP
+int sched_autogroup_handler(struct ctl_table *table, int write,
+		void __user *buffer, size_t *lenp,
+		loff_t *ppos);
+
+extern unsigned int sysctl_sched_autogroup_enabled;
+
+void sched_autogroup_create_tty(struct tty_struct *tty);
+void sched_autogroup_destroy_tty(struct tty_struct *tty);
+#else
+static inline void sched_autogroup_create_tty(struct tty_struct *tty) { }
+static inline void sched_autogroup_destroy_tty(struct tty_struct *tty) { }
+#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);
include/linux/tty.h +3 -0
Index: linux-2.6.36.git/include/linux/tty.h
===================================================================
@@ -327,6 +327,9 @@ struct tty_struct {
 	/* If the tty has a pending do_SAK, queue it here - akpm */
 	struct work_struct SAK_work;
 	struct tty_port *port;
+#ifdef CONFIG_SCHED_AUTOGROUP
+	struct task_group *tg;
+#endif
 };
 
 /* Each of a tty's open files has private_data pointing to tty_file_private */
kernel/sched.c +8 -1
Index: linux-2.6.36.git/kernel/sched.c
===================================================================
@@ -78,6 +78,7 @@
 
 #include "sched_cpupri.h"
 #include "workqueue_sched.h"
+#include "sched_autogroup.h"
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/sched.h>
@@ -612,11 +613,16 @@ static inline int cpu_of(struct rq *rq)
  */
 static inline struct task_group *task_group(struct task_struct *p)
 {
+	struct task_group *tg;
 	struct cgroup_subsys_state *css;
 
 	css = task_subsys_state_check(p, cpu_cgroup_subsys_id,
 			lockdep_is_held(&task_rq(p)->lock));
-	return container_of(css, struct task_group, css);
+	tg = container_of(css, struct task_group, css);
+
+	autogroup_check_attach(p, &tg);
+
+	return tg;
 }
 
 /* Change a task's cfs_rq and parent entity if it moves across CPUs/groups */
@@ -1920,6 +1926,7 @@ static void deactivate_task(struct rq *r
 #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
drivers/char/tty_io.c +2 -0
Index: linux-2.6.36.git/drivers/char/tty_io.c
===================================================================
@@ -185,6 +185,7 @@ void free_tty_struct(struct tty_struct *
 {
 	kfree(tty->write_buf);
 	tty_buffer_free_all(tty);
+	sched_autogroup_destroy_tty(tty);
 	kfree(tty);
 }
 
@@ -2823,6 +2824,7 @@ void initialize_tty_struct(struct tty_st
 	tty->ops = driver->ops;
 	tty->index = idx;
 	tty_line_name(driver, idx, tty->name);
+	sched_autogroup_create_tty(tty);
 }
 
 /**
kernel/sched_autogroup.h new file +7 -0
Index: linux-2.6.36.git/kernel/sched_autogroup.h
===================================================================
@@ -0,0 +1,7 @@
+#ifdef CONFIG_SCHED_AUTOGROUP
+static inline void
+autogroup_check_attach(struct task_struct *p, struct task_group **tg);
+#else
+static inline void
+autogroup_check_attach(struct task_struct *p, struct task_group **tg) { }
+#endif
kernel/sysctl.c +11 -0
Index: linux-2.6.36.git/kernel/sysctl.c
===================================================================
@@ -384,6 +384,17 @@ static struct ctl_table kern_table[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec,
 	},
+#ifdef CONFIG_SCHED_AUTOGROUP
+	{
+		.procname	= "sched_autogroup_enabled",
+		.data		= &sysctl_sched_autogroup_enabled,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= sched_autogroup_handler,
+		.extra1		= &zero,
+		.extra2		= &one,
+	},
+#endif
 #ifdef CONFIG_PROVE_LOCKING
 	{
 		.procname	= "prove_locking",
init/Kconfig +13 -0
Index: linux-2.6.36.git/init/Kconfig
===================================================================
@@ -652,6 +652,19 @@ config DEBUG_BLK_CGROUP
 
 endif # CGROUPS
 
+config SCHED_AUTOGROUP
+	bool "Automatic process group scheduling"
+	select CGROUPS
+	select CGROUP_SCHED
+	select FAIR_GROUP_SCHED
+	select BLK_CGROUP
+	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
 
kernel/sched_autogroup.c new file +76 -0

76 lines not on this page · raw message

Subject: sched, cgroup: Fixup broken cgroup movement
From: Peter Zijlstra <[email protected]>
Date: Fri Oct 15 15:24:15 CEST 2010

---
 include/linux/sched.h |    2 +-
 kernel/sched.c        |    8 ++++----
 kernel/sched_fair.c   |   25 +++++++++++++++++++------
 3 files changed, 24 insertions(+), 11 deletions(-)
kernel/sched.c +4 -4
Index: linux-2.6.36.git/kernel/sched.c
===================================================================
@@ -8297,12 +8297,12 @@ void sched_move_task(struct task_struct
 	if (unlikely(running))
 		tsk->sched_class->put_prev_task(rq, tsk);
 
-	set_task_rq(tsk, task_cpu(tsk));
-
 #ifdef CONFIG_FAIR_GROUP_SCHED
-	if (tsk->sched_class->moved_group)
-		tsk->sched_class->moved_group(tsk, on_rq);
+	if (tsk->sched_class->task_move_group)
+		tsk->sched_class->task_move_group(tsk, on_rq);
+	else
 #endif
+		set_task_rq(tsk, task_cpu(tsk));
 
 	if (unlikely(running))
 		tsk->sched_class->set_curr_task(rq);
include/linux/sched.h +1 -1
Index: linux-2.6.36.git/include/linux/sched.h
===================================================================
@@ -1072,7 +1072,7 @@ struct sched_class {
 					 struct task_struct *task);
 
 #ifdef CONFIG_FAIR_GROUP_SCHED
-	void (*moved_group) (struct task_struct *p, int on_rq);
+	void (*task_move_group) (struct task_struct *p, int on_rq);
 #endif
 };
 
kernel/sched_fair.c +19 -6
Index: linux-2.6.36.git/kernel/sched_fair.c
===================================================================
@@ -3824,13 +3824,26 @@ static void set_curr_task_fair(struct rq
 }
 
 #ifdef CONFIG_FAIR_GROUP_SCHED
-static void moved_group_fair(struct task_struct *p, int on_rq)
+static void task_move_group_fair(struct task_struct *p, int on_rq)
 {
-	struct cfs_rq *cfs_rq = task_cfs_rq(p);
-
-	update_curr(cfs_rq);
+	/*
+	 * If the task was not on the rq at the time of this cgroup movement
+	 * it must have been asleep, sleeping tasks keep their ->vruntime
+	 * absolute on their old rq until wakeup (needed for the fair sleeper
+	 * bonus in place_entity()).
+	 *
+	 * If it was on the rq, we've just 'preempted' it, which does convert
+	 * ->vruntime to a relative base.
+	 *
+	 * Make sure both cases convert their relative position when migrating
+	 * to another cgroup's rq. This does somewhat interfere with the
+	 * fair sleeper stuff for the first placement, but who cares.
+	 */
+	if (!on_rq)
+		p->se.vruntime -= cfs_rq_of(&p->se)->min_vruntime;
+	set_task_rq(p, task_cpu(p));
 	if (!on_rq)
-		place_entity(cfs_rq, &p->se, 1);
+		p->se.vruntime += cfs_rq_of(&p->se)->min_vruntime;
 }
 #endif
 
@@ -3882,7 +3895,7 @@ static const struct sched_class fair_sch
 	.get_rr_interval	= get_rr_interval_fair,
 
 #ifdef CONFIG_FAIR_GROUP_SCHED
-	.moved_group		= moved_group_fair,
+	.task_move_group	= task_move_group_fair,
 #endif
 };
 
#17 lore raw
Quoted 5 lines · Mike Galbraith
Quoted 2 lines · Ingo Molnar

Mind doing more of the tty->desktop renames/generalizations as Linus suggested, and resend the patch?

Quoted 8 lines

Here she comes. Better/Worse?

Changes:
- tty->autogroup.
- only autogroup fair class tasks.
- removed dainbramaged sleeper vruntime twiddling.
- removed paranoid locking.
- removed noop detatch code.

I really like the new 'autogroup scheduler' name - as we really dont want to turn this into anything but an intelligent grouping thing. Via the naming we can resist heuristics for example.

Btw., how does Xorg fare with this? Can we remove sleeper fairness for example and simplify other bits of the CFS logic as a side-effect?

Ingo

#18 lore raw
Quoted 2 lines · Ingo Molnar

Btw., how does Xorg fare with this? Can we remove sleeper fairness for example and simplify other bits of the CFS logic as a side-effect?

Works a treat for me. As I write this in evolution, I have amarok playing with a visualization and a make -j100 running. Song switch is instant, visualization is nice and smooth despite unaccelerated Xorg.

We can't whack fair sleepers though, not without inventing a new preemption model to take it's place.

-Mike

#19 lore raw
In reply to Mike Galbraith
Quoted 19 lines · Mike Galbraith
Quoted 2 lines · Ingo Molnar

Mind doing more of the tty->desktop renames/generalizations as Linus suggested, and resend the patch?

Here she comes. Better/Worse?

Changes:
- tty->autogroup.
- only autogroup fair class tasks.
- removed dainbramaged sleeper vruntime twiddling.
- removed paranoid locking.
- removed noop detatch code.

Quoted 5 lines

I'd also suggest to move it out of EXPERIMENTAL - we dont really do that for core kernel features as most distros enable CONFIG_EXPERIMENTAL so it's a rather meaningless distinction. Since the feature is default-n, people will get the old scheduler by default but can also choose this desktop-centric scheduling mode.

Quoted 8 lines
Quoted 2 lines

I'd even argue to make it default-y, because this patch clearly cures a form of kbuild cancer.

You top dogs can make the default call.. it it's accepted that is ;-)

Marcus: care to try the below? Works fine for me (but so did first cut). It depends on the attached patch, and applied to virgin shiny new 2.6.36.

Works fine interactively, but I still get the same kernel BUG on reboot time as before. Would a photo of the trace help you?

Signature · 1 line
Markus
#20 lore raw
Quoted 23 lines · Markus Trippelsdorf
Quoted 22 lines · Mike Galbraith
Quoted 2 lines · Ingo Molnar

Mind doing more of the tty->desktop renames/generalizations as Linus suggested, and resend the patch?

Here she comes. Better/Worse?

Changes:
- tty->autogroup.
- only autogroup fair class tasks.
- removed dainbramaged sleeper vruntime twiddling.
- removed paranoid locking.
- removed noop detatch code.

Quoted 7 lines

I'd also suggest to move it out of EXPERIMENTAL - we dont really do that for core kernel features as most distros enable CONFIG_EXPERIMENTAL so it's a rather meaningless distinction. Since the feature is default-n, people will get the old scheduler by default but can also choose this desktop-centric scheduling mode.

I'd even argue to make it default-y, because this patch clearly cures a form of kbuild cancer.

Quoted 8 lines
Quoted 5 lines

You top dogs can make the default call.. it it's accepted that is ;-)

Marcus: care to try the below? Works fine for me (but so did first cut). It depends on the attached patch, and applied to virgin shiny new 2.6.36.

Works fine interactively, but I still get the same kernel BUG on reboot time as before. Would a photo of the trace help you?

Odd. Yeah, please send me the photo and your .config.

-Mike

A message that is not in the archive (sent off-list or not yet received): [email protected]

#22 lore raw
Quoted 31 lines · * Markus Trippelsdorf ([email protected]) wrote:
Quoted 30 lines · Mike Galbraith
Quoted 29 lines · Markus Trippelsdorf
Quoted 27 lines · Mike Galbraith
Quoted 2 lines · Ingo Molnar

Mind doing more of the tty->desktop renames/generalizations as Linus suggested, and resend the patch?

Here she comes. Better/Worse?

Changes:
- tty->autogroup.
- only autogroup fair class tasks.
- removed dainbramaged sleeper vruntime twiddling.
- removed paranoid locking.
- removed noop detatch code.

Quoted 7 lines

I'd also suggest to move it out of EXPERIMENTAL - we dont really do that for core kernel features as most distros enable CONFIG_EXPERIMENTAL so it's a rather meaningless distinction. Since the feature is default-n, people will get the old scheduler by default but can also choose this desktop-centric scheduling mode.

I'd even argue to make it default-y, because this patch clearly cures a form of kbuild cancer.

You top dogs can make the default call.. it it's accepted that is ;-)

Marcus: care to try the below? Works fine for me (but so did first cut). It depends on the attached patch, and applied to virgin shiny new 2.6.36.

Quoted 6 lines
Quoted 4 lines
Quoted 2 lines

Works fine interactively, but I still get the same kernel BUG on reboot time as before. Would a photo of the trace help you?

Odd. Yeah, please send me the photo and your .config.

OK here you go. Both are attached.

In the backtrace, the scheduler is called from:

do_group_exit()
__dequeue_signal()

    do_exit()

given that task_group() is called from many spots in the scheduler, I wonder if some checks making sure that tg != NULL in task_group() would be appropriate ? Also checking that p->signal is non-NULL in autogroup_attach_tty() might help.

Thanks,

Mathieu

Signature · 4 lines
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
#23 lore raw
In reply to Mike Galbraith

* Mike Galbraith ([email protected]) wrote:
[...]

Quoted 15 lines
kernel/sched_autogroup.c new file +15 -0
+static void
+autogroup_attach_tty(struct task_struct *p, struct task_group **tg)
+{
+	struct tty_struct *tty = p->signal->tty;
+
+	if (!tty)
+		return;
+
+	*tg = p->signal->tty->tg;
+}
+
+static inline void
+autogroup_check_attach(struct task_struct *p, struct task_group **tg)
+{
+	if (!sysctl_sched_autogroup_enabled || *tg != &root_task_group ||

kernel/sched_autogroup.c line 47

Quoted 10 lines
kernel/sched_autogroup.c new file +10 -0
+			p->sched_class != &fair_sched_class)
+		return;
+
+	rcu_read_lock();
+
+	autogroup_attach_tty(p, tg);
+
+	rcu_read_unlock();
+}
+

Hi Mike,

This per-tty task grouping approach looks very promising. I'll give it a spin when I find the time. Meanwhile, a little question about locking here: how is the read lock supposed to protect from p->signal (and p->signal->tty) modifications ? What's the locking scheme here ? So maybe just simple rcu_dereference are missing, or maybe the tsk->sighand->siglock might be required. In all cases, I feel something is missing there.

Thanks!

Mathieu

Signature · 4 lines
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
#24 lore raw
Quoted 25 lines · Mathieu Desnoyers

* Mike Galbraith ([email protected]) wrote:
[...]

Quoted 23 lines
kernel/sched_autogroup.c new file +23 -0
+static void
+autogroup_attach_tty(struct task_struct *p, struct task_group **tg)
+{
+	struct tty_struct *tty = p->signal->tty;
+
+	if (!tty)
+		return;
+
+	*tg = p->signal->tty->tg;
+}
+
+static inline void
+autogroup_check_attach(struct task_struct *p, struct task_group **tg)
+{
+	if (!sysctl_sched_autogroup_enabled || *tg != &root_task_group ||
+			p->sched_class != &fair_sched_class)
+		return;
+
+	rcu_read_lock();
+
+	autogroup_attach_tty(p, tg);
+
+	rcu_read_unlock();
Quoted 8 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+}
+

Meanwhile, a little question about locking here: how is
the read lock supposed to protect from p->signal (and p->signal->tty) modifications ? What's the locking scheme here ? So maybe just simple rcu_dereference are missing, or maybe the tsk->sighand->siglock might be required. In all cases, I feel something is missing there.

Oleg, could you comment?

#25 lore raw
Quoted 4 lines · Peter Zijlstra
Quoted 2 lines · Mathieu Desnoyers

* Mike Galbraith ([email protected]) wrote:
[...]

Quoted 10 lines
Quoted 10 lines

kernel/sched_autogroup.c line 32

Quoted 10 lines
kernel/sched_autogroup.c new file +10 -0
+static void
+autogroup_attach_tty(struct task_struct *p, struct task_group **tg)
+{
+	struct tty_struct *tty = p->signal->tty;
+
+	if (!tty)
+		return;
+
+	*tg = p->signal->tty->tg;
+}

minor nit, I think in theory this needs barrier(), or

	struct tty_struct *tty = ACCESS_ONCE(p->signal->tty);

	if (tty)
		*tg = tty->tg;
Quoted 21 lines
Quoted 12 lines
Quoted 12 lines
kernel/sched_autogroup.c new file +12 -0
+static inline void
+autogroup_check_attach(struct task_struct *p, struct task_group **tg)
+{
+	if (!sysctl_sched_autogroup_enabled || *tg != &root_task_group ||
+			p->sched_class != &fair_sched_class)
+		return;
+
+	rcu_read_lock();
+
+	autogroup_attach_tty(p, tg);
+
+	rcu_read_unlock();
Quoted 8 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+}
+

Meanwhile, a little question about locking here: how is
the read lock supposed to protect from p->signal (and p->signal->tty) modifications ? What's the locking scheme here ? So maybe just simple rcu_dereference are missing, or maybe the tsk->sighand->siglock might be required. In all cases, I feel something is missing there.

Quoted 1 line

Oleg, could you comment?

No, I don't understand this ;) But I know nothig about task groups, most probably this is OK.

It is not clear to me why do we need rcu_read_lock() and how it can help. The tty can go away right after dereferencing signal->tty.

Even if the task doesn't exit, it (or its sub-thread) can do sys_setsid() at any moment and free this tty. If any thread was moved to tty->sg, doesn't this mean that, say, ->cfs_rq will point to the already freed tg->cfs_rq?

Quoted 1 line

From http://marc.info/?l=linux-kernel&m=128764874422614

	+int sched_autogroup_handler(struct ctl_table *table, int write,
	+		void __user *buffer, size_t *lenp, loff_t *ppos)
	+{
	+	struct task_struct *p, *t;
	+	struct task_group *tg;
	+	int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
	+
	+	if (ret || !write)
	+		return ret;
	+
	+	for_each_process(p) {

Hmm. This needs rcu lock at least?

	+		tg = task_group(p);

Why?

	+		sched_move_task(p);
	+		list_for_each_entry_rcu(t, &p->thread_group, thread_group) {
	+			sched_move_task(t);
	+		}
	+	}

Looks like, you can just do

	do_each_thread(p, t) {
		sched_move_task(t);
	} while_each_thread(p, t);

With the same effect.

Oleg.

#26 lore raw
Quoted 18 lines · Oleg Nesterov
Quoted 4 lines · Peter Zijlstra
Quoted 2 lines · Mathieu Desnoyers

* Mike Galbraith ([email protected]) wrote:
[...]

Quoted 10 lines
Quoted 10 lines

kernel/sched_autogroup.c line 32

Quoted 10 lines
kernel/sched_autogroup.c new file +10 -0
+static void
+autogroup_attach_tty(struct task_struct *p, struct task_group **tg)
+{
+	struct tty_struct *tty = p->signal->tty;
+
+	if (!tty)
+		return;
+
+	*tg = p->signal->tty->tg;
+}

minor nit, I think in theory this needs barrier(), or

Quoted 4 lines
	struct tty_struct *tty = ACCESS_ONCE(p->signal->tty);

	if (tty)
		*tg = tty->tg;

Thanks.

Quoted 21 lines
Quoted 21 lines
Quoted 20 lines
Quoted 4 lines
kernel/sched_autogroup.c new file +4 -0
+static inline void
+autogroup_check_attach(struct task_struct *p, struct task_group **tg)
+{
+	if (!sysctl_sched_autogroup_enabled || *tg != &root_task_group ||

kernel/sched_autogroup.c line 47

Quoted 10 lines
kernel/sched_autogroup.c new file +10 -0
+			p->sched_class != &fair_sched_class)
+		return;
+
+	rcu_read_lock();
+
+	autogroup_attach_tty(p, tg);
+
+	rcu_read_unlock();
+}
+

Meanwhile, a little question about locking here: how is
the read lock supposed to protect from p->signal (and p->signal->tty) modifications ? What's the locking scheme here ? So maybe just simple rcu_dereference are missing, or maybe the tsk->sighand->siglock might be required. In all cases, I feel something is missing there.

Quoted 7 lines
Quoted 1 line

Oleg, could you comment?

No, I don't understand this ;) But I know nothig about task groups, most probably this is OK.

It is not clear to me why do we need rcu_read_lock() and how it can help. The tty can go away right after dereferencing signal->tty.

It was inherited.

Quoted 3 lines

Even if the task doesn't exit, it (or its sub-thread) can do sys_setsid() at any moment and free this tty. If any thread was moved to tty->sg, doesn't this mean that, say, ->cfs_rq will point to the already freed tg->cfs_rq?

Ah, so isn't as safe as it looked. Thanks!

Quoted 14 lines
Quoted 1 line

From http://marc.info/?l=linux-kernel&m=128764874422614

	+int sched_autogroup_handler(struct ctl_table *table, int write,
	+		void __user *buffer, size_t *lenp, loff_t *ppos)
	+{
	+	struct task_struct *p, *t;
	+	struct task_group *tg;
	+	int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
	+
	+	if (ret || !write)
	+		return ret;
	+
	+	for_each_process(p) {
Quoted 1 line

Hmm. This needs rcu lock at least?

(used to be paranoid locking there.. vs required locking)

Quoted 3 lines
	+		tg = task_group(p);

Why?

A cleanup leftover.

Quoted 7 lines
	+		sched_move_task(p);
	+		list_for_each_entry_rcu(t, &p->thread_group, thread_group) {
	+			sched_move_task(t);
	+		}
	+	}
Quoted 7 lines

Looks like, you can just do

	do_each_thread(p, t) {
		sched_move_task(t);
	} while_each_thread(p, t);

With the same effect.

Yeah.

So in theory, the tty can go away on me. I knew this was too easy.

-Mike

#27 lore raw

PATCH v3 1/1 sched: automated per tty task groups

In reply to Oleg Nesterov
Quoted 2 lines · Oleg Nesterov

It is not clear to me why do we need rcu_read_lock() and how it can help. The tty can go away right after dereferencing signal->tty.

Which was Marcus' crash. Didn't happen here only because I didn't have CONFIG_PREEMPT set.

Changes since v2:
- drop

#28 lore raw
Quoted 10 lines · Mike Galbraith
Quoted 2 lines · Oleg Nesterov

It is not clear to me why do we need rcu_read_lock() and how it can help. The tty can go away right after dereferencing signal->tty.

Which was Marcus' crash. Didn't happen here only because I didn't have CONFIG_PREEMPT set.

Changes since v2:
- drop

Bumped mouse, message escaped.

Doesn't matter though, damn thing just blew up during enable/disable plus hackbench stress test, despite holding a reference to the tty at every place tty changes (under sighand lock), and moving the task with that reference held.

CONFIG_PREEMPT is being a little S.O.B.

-Mike

#29 lore raw
Quoted 12 lines · Mike Galbraith
Quoted 10 lines · Mike Galbraith
Quoted 2 lines · Oleg Nesterov

It is not clear to me why do we need rcu_read_lock() and how it can help. The tty can go away right after dereferencing signal->tty.

Which was Marcus' crash.  Didn't happen here only because I didn't have CONFIG_PREEMPT set.

Changes since v2:
  - drop

Quoted 6 lines

Bumped mouse, message escaped.

Doesn't matter though, damn thing just blew up during enable/disable plus hackbench stress test, despite holding a reference to the tty at every place tty changes (under sighand lock), and moving the task with that reference held.

So I have a suggestion that may not be popular with you, because it does end up changing the approach of your patch a lot.

And I have to say, I like how your last patch looked. It was surprisingly small, simple, and clean. So I hate saying "I think it should perhaps do things a bit differently". That said, I would suggest:

- don't depend on "tsk->signal->tty" at all.

- INSTEAD, introduce a "tsk->signal->sched_group" pointer that points
to whatever the current auto-task_group is. Remember, long-term, we'd want to maybe have other heuristics than just the tty groups, so we'd want this separate from the tty logic _anyway_

- at fork time, just copy the task_group pointer in copy_signal() if
it is non-NULL, and increment the refcount (I don't think struct task_group is refcounted now, but this would require it).

- at free_signal_struct(), just do a
"put_task_group(sig->task_group);" before freeing it.

- make the scheduler use the "tsk->signal->sched_group" as the
default group if nothing else exists.

Now, all the basic logic is _entirely_ unaware of any tty logic, and it's generic. And none of it has any races with some odd tty release logic or anything like that.

Now, after this, the only thing you'd need to do is hook into __proc_set_tty(), which already holds the sighand lock, and _there_ you would attach the task_group to the process. Notice how it would never be attached to a tty at all, so tty_release etc would never be involved in any taskgroup thing - it's not really the tty that owns the taskgroup, it's simply the act of becoming a tty task group leader that attaches the task to a new scheduling group.

It also means, for example, that if a process loses its tty (and doesn't get a new one - think hangup), it still remains in whatever scheduling group it started out with. The tty really is immaterial.

And the nice thing about this is that it should be trivial to make other things than tty's trigger this same thing, if we find a pattern (or create some new interface to let people ask for it) for something that should create a new group (like perhaps spawning a graphical application from the window manager rather than from a tty).

Comments?

Linus

#30 lore raw
Quoted 15 lines · Linus Torvalds
Quoted 14 lines · Mike Galbraith
Quoted 10 lines · Mike Galbraith
Quoted 2 lines · Oleg Nesterov

It is not clear to me why do we need rcu_read_lock() and how it can help. The tty can go away right after dereferencing signal->tty.

Which was Marcus' crash. Didn't happen here only because I didn't have CONFIG_PREEMPT set.

Changes since v2:
- drop

Bumped mouse, message escaped.

Quoted 7 lines
Quoted 4 lines

Doesn't matter though, damn thing just blew up during enable/disable plus hackbench stress test, despite holding a reference to the tty at every place tty changes (under sighand lock), and moving the task with that reference held.

So I have a suggestion that may not be popular with you, because it does end up changing the approach of your patch a lot.

Suggestions highly welcome. The raciness is driving me nuts. I can't afford additional locking, and barriers ain't working.

Quoted 38 lines

And I have to say, I like how your last patch looked. It was surprisingly small, simple, and clean. So I hate saying "I think it should perhaps do things a bit differently". That said, I would suggest:

- don't depend on "tsk->signal->tty" at all.

- INSTEAD, introduce a "tsk->signal->sched_group" pointer that points
to whatever the current auto-task_group is. Remember, long-term, we'd want to maybe have other heuristics than just the tty groups, so we'd want this separate from the tty logic _anyway_

- at fork time, just copy the task_group pointer in copy_signal() if
it is non-NULL, and increment the refcount (I don't think struct task_group is refcounted now, but this would require it).

- at free_signal_struct(), just do a
"put_task_group(sig->task_group);" before freeing it.

- make the scheduler use the "tsk->signal->sched_group" as the
default group if nothing else exists.

Now, all the basic logic is _entirely_ unaware of any tty logic, and it's generic. And none of it has any races with some odd tty release logic or anything like that.

Now, after this, the only thing you'd need to do is hook into __proc_set_tty(), which already holds the sighand lock, and _there_ you would attach the task_group to the process. Notice how it would never be attached to a tty at all, so tty_release etc would never be involved in any taskgroup thing - it's not really the tty that owns the taskgroup, it's simply the act of becoming a tty task group leader that attaches the task to a new scheduling group.

It also means, for example, that if a process loses its tty (and doesn't get a new one - think hangup), it still remains in whatever scheduling group it started out with. The tty really is immaterial.

Quoted 7 lines

And the nice thing about this is that it should be trivial to make other things than tty's trigger this same thing, if we find a pattern (or create some new interface to let people ask for it) for something that should create a new group (like perhaps spawning a graphical application from the window manager rather than from a tty).

Comments?

Much more tasteful than what I was about to do as a last resort funky race killer, namely make my on/off switch a machine wide atomic bomb :)

Thanks!

-Mike

#31 lore raw
In reply to Linus Torvalds

Greetings from sunny Arizona!

Quoted 41 lines · Linus Torvalds

So I have a suggestion that may not be popular with you, because it does end up changing the approach of your patch a lot.

And I have to say, I like how your last patch looked. It was surprisingly small, simple, and clean. So I hate saying "I think it should perhaps do things a bit differently". That said, I would suggest:

- don't depend on "tsk->signal->tty" at all.

- INSTEAD, introduce a "tsk->signal->sched_group" pointer that points
to whatever the current auto-task_group is. Remember, long-term, we'd want to maybe have other heuristics than just the tty groups, so we'd want this separate from the tty logic _anyway_

- at fork time, just copy the task_group pointer in copy_signal() if
it is non-NULL, and increment the refcount (I don't think struct task_group is refcounted now, but this would require it).

- at free_signal_struct(), just do a
"put_task_group(sig->task_group);" before freeing it.

- make the scheduler use the "tsk->signal->sched_group" as the
default group if nothing else exists.

Now, all the basic logic is _entirely_ unaware of any tty logic, and it's generic. And none of it has any races with some odd tty release logic or anything like that.

Now, after this, the only thing you'd need to do is hook into __proc_set_tty(), which already holds the sighand lock, and _there_ you would attach the task_group to the process. Notice how it would never be attached to a tty at all, so tty_release etc would never be involved in any taskgroup thing - it's not really the tty that owns the taskgroup, it's simply the act of becoming a tty task group leader that attaches the task to a new scheduling group.

It also means, for example, that if a process loses its tty (and doesn't get a new one - think hangup), it still remains in whatever scheduling group it started out with. The tty really is immaterial.

Quoted 7 lines

And the nice thing about this is that it should be trivial to make other things than tty's trigger this same thing, if we find a pattern (or create some new interface to let people ask for it) for something that should create a new group (like perhaps spawning a graphical application from the window manager rather than from a tty).

Comments?

I _finally_ got back to this yesterday, and implemented your suggestion, though with a couple minor variations. Putting the autogroup pointer in the signal struct didn't look right to me, so I plugged it into the task struct instead. I also didn't refcount taskgroups, wanted the patchlet to be as self-contained as possible, so refcounted the autogroup struct instead. I also left group movement on tty disassociation in place, but may nuke it.

The below has withstood an all night thrashing in my laptop with a PREEMPT_RT kernel, and looks kinda presentable to me, so...

A recurring complaint from CFS users is that parallel kbuild has a negative impact on desktop interactivity. This patch implements an idea from Linus, to automatically create task groups. This patch only implements Linus' per tty task group suggestion, and only for fair class tasks, but leaves the way open for enhancement.

Implementation: each task struct contains an inherited pointer to a refcounted autogroup struct containing a task group pointer, the default for all tasks pointing to the init_task_group. When a task calls __proc_set_tty(), the task's reference to the default group is dropped, a new task group is created, and the task is moved out of the old group and into the new. Children thereafter inherit this task group, and increase it's refcount. Calls to __tty_hangup() and proc_clear_tty() move the caller back to the init_task_group, and possibly destroy the task group. On exit, reference to the current task group is dropped, and the task group is potentially destroyed. At runqueue selection time, iff a task has no cgroup assignment, it's current autogroup is used.

The feature is enabled from boot by default if CONFIG_SCHED_AUTOGROUP is selected, but can be disabled via the boot option noautogroup, and can be also be turned on/off on the fly via..
echo [01] > /proc/sys/kernel/sched_autogroup_enabled.
..which will automatically move tasks to/from the root task group.

Some numbers.

A 100% hog overhead measurement proggy pinned to the same CPU as a make -j10

About measurement proggy:
pert/sec = perturbations/sec
min/max/avg = scheduler service latencies in usecs
sum/s = time accrued by the competition per sample period (1 sec here) overhead = %CPU received by the competition per sample period

pert/s:       31 >40475.37us:        3 min:  0.37 max:48103.60 avg:29573.74 sum/s:916786us overhead:90.24%
pert/s:       23 >41237.70us:       12 min:  0.36 max:56010.39 avg:40187.01 sum/s:924301us overhead:91.99%
pert/s:       24 >42150.22us:       12 min:  8.86 max:61265.91 avg:39459.91 sum/s:947038us overhead:92.20%
pert/s:       26 >42344.91us:       11 min:  3.83 max:52029.60 avg:36164.70 sum/s:940282us overhead:91.12%
pert/s:       24 >44262.90us:       14 min:  5.05 max:82735.15 avg:40314.33 sum/s:967544us overhead:92.22%

Same load with this patch applied.

pert/s:      229 >5484.43us:       41 min:  0.15 max:12069.42 avg:2193.81 sum/s:502382us overhead:50.24%
pert/s:      222 >5652.28us:       43 min:  0.46 max:12077.31 avg:2248.56 sum/s:499181us overhead:49.92%
pert/s:      211 >5809.38us:       43 min:  0.16 max:12064.78 avg:2381.70 sum/s:502538us overhead:50.25%
pert/s:      223 >6147.92us:       43 min:  0.15 max:16107.46 avg:2282.17 sum/s:508925us overhead:50.49%
pert/s:      218 >6252.64us:       43 min:  0.16 max:12066.13 avg:2324.11 sum/s:506656us overhead:50.27%

Average service latency is an order of magnitude better with autogroup. (Imagine that pert were Xorg or whatnot instead)

Using Mathieu Desnoyers' wakeup-latency testcase:

With taskset -c 3 make -j 10 running..

taskset -c 3 ./wakeup-latency& sleep 30;killall wakeup-latency

without:
maximum latency: 42963.2 µs
average latency: 9077.0 µs
missed timer events: 0

with:
maximum latency: 4160.7 µs
average latency: 149.4 µs
missed timer events: 0

---
 Documentation/kernel-parameters.txt |    2 
 drivers/char/tty_io.c               |    4 
 include/linux/sched.h               |   20 ++++
 init/Kconfig                        |   12 ++
 kernel/exit.c                       |    1 
 kernel/sched.c                      |   28 ++++--
 kernel/sched_autogroup.c            |  161 ++++++++++++++++++++++++++++++++++++
 kernel/sched_autogroup.h            |   10 ++
 kernel/sysctl.c                     |   11 ++
 9 files changed, 241 insertions(+), 8 deletions(-)
include/linux/sched.h +20 -0
Index: linux-2.6.36.git/include/linux/sched.h
===================================================================
@@ -1159,6 +1159,7 @@ struct sched_rt_entity {
 };
 
 struct rcu_node;
+struct autogroup;
 
 struct task_struct {
 	volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
@@ -1181,6 +1182,10 @@ struct task_struct {
 	struct sched_entity se;
 	struct sched_rt_entity rt;
 
+#ifdef CONFIG_SCHED_AUTOGROUP
+	struct autogroup *autogroup;
+#endif
+
 #ifdef CONFIG_PREEMPT_NOTIFIERS
 	/* list of struct preempt_notifier: */
 	struct hlist_head preempt_notifiers;
@@ -1900,6 +1905,21 @@ 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;
+
+int sched_autogroup_handler(struct ctl_table *table, int write,
+		void __user *buffer, size_t *lenp, loff_t *ppos);
+
+extern void sched_autogroup_create_attach(struct task_struct *p);
+extern void sched_autogroup_detatch(struct task_struct *p);
+extern void sched_autogroup_exit(struct task_struct *p);
+#else
+static inline void sched_autogroup_create_attach(struct task_struct *p) { }
+static inline void sched_autogroup_detatch(struct task_struct *p) { }
+static inline void sched_autogroup_exit(struct task_struct *p) { }
+#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);
kernel/sched.c +20 -8
Index: linux-2.6.36.git/kernel/sched.c
===================================================================
@@ -78,6 +78,7 @@
 
 #include "sched_cpupri.h"
 #include "workqueue_sched.h"
+#include "sched_autogroup.h"
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/sched.h>
@@ -612,11 +613,16 @@ static inline int cpu_of(struct rq *rq)
  */
 static inline struct task_group *task_group(struct task_struct *p)
 {
+	struct task_group *tg;
 	struct cgroup_subsys_state *css;
 
 	css = task_subsys_state_check(p, cpu_cgroup_subsys_id,
 			lockdep_is_held(&task_rq(p)->lock));
-	return container_of(css, struct task_group, css);
+	tg = container_of(css, struct task_group, css);
+
+	autogroup_task_group(p, &tg);
+
+	return tg;
 }
 
 /* Change a task's cfs_rq and parent entity if it moves across CPUs/groups */
@@ -1920,6 +1926,7 @@ static void deactivate_task(struct rq *r
 #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
@@ -2569,6 +2576,7 @@ void sched_fork(struct task_struct *p, i
 	 * Silence PROVE_RCU.
 	 */
 	rcu_read_lock();
+	autogroup_fork(p);
 	set_task_cpu(p, cpu);
 	rcu_read_unlock();
 
@@ -7749,7 +7757,7 @@ void __init sched_init(void)
 #ifdef CONFIG_CGROUP_SCHED
 	list_add(&init_task_group.list, &task_groups);
 	INIT_LIST_HEAD(&init_task_group.children);
-
+	autogroup_init(&init_task);
 #endif /* CONFIG_CGROUP_SCHED */
 
 #if defined CONFIG_FAIR_GROUP_SCHED && defined CONFIG_SMP
@@ -8279,15 +8287,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);
 
 	running = task_current(rq, tsk);
 	on_rq = tsk->se.on_rq;
@@ -8308,7 +8312,15 @@ void sched_move_task(struct task_struct
 		tsk->sched_class->set_curr_task(rq);
 	if (on_rq)
 		enqueue_task(rq, tsk, 0);
+}
+
+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);
 }
 #endif /* CONFIG_CGROUP_SCHED */
drivers/char/tty_io.c +4 -0
Index: linux-2.6.36.git/drivers/char/tty_io.c
===================================================================
@@ -580,6 +580,7 @@ void __tty_hangup(struct tty_struct *tty
 			spin_lock_irq(&p->sighand->siglock);
 			if (p->signal->tty == tty) {
 				p->signal->tty = NULL;
+				sched_autogroup_detatch(p);
 				/* We defer the dereferences outside fo
 				   the tasklist lock */
 				refs++;
@@ -3070,6 +3071,7 @@ void proc_clear_tty(struct task_struct *
 	spin_lock_irqsave(&p->sighand->siglock, flags);
 	tty = p->signal->tty;
 	p->signal->tty = NULL;
+	sched_autogroup_detatch(p);
 	spin_unlock_irqrestore(&p->sighand->siglock, flags);
 	tty_kref_put(tty);
 }
@@ -3089,12 +3091,14 @@ static void __proc_set_tty(struct task_s
 		tty->session = get_pid(task_session(tsk));
 		if (tsk->signal->tty) {
 			printk(KERN_DEBUG "tty not NULL!!\n");
+			sched_autogroup_detatch(tsk);
 			tty_kref_put(tsk->signal->tty);
 		}
 	}
 	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)
kernel/exit.c +1 -0
Index: linux-2.6.36.git/kernel/exit.c
===================================================================
@@ -174,6 +174,7 @@ repeat:
 	write_lock_irq(&tasklist_lock);
 	tracehook_finish_release_task(p);
 	__exit_signal(p);
+	sched_autogroup_exit(p);
 
 	/*
 	 * If we are the last non-leader member of the thread
kernel/sched_autogroup.h new file +10 -0
Index: linux-2.6.36.git/kernel/sched_autogroup.h
===================================================================
@@ -0,0 +1,10 @@
+#ifdef CONFIG_SCHED_AUTOGROUP
+static inline void
+autogroup_task_group(struct task_struct *p, struct task_group **tg);
+static void __sched_move_task(struct task_struct *tsk, struct rq *rq);
+#else /* !CONFIG_SCHED_AUTOGROUP */
+static inline void autogroup_init(struct task_struct *init_task) {  }
+static inline void autogroup_fork(struct task_struct *p) { }
+static inline void
+autogroup_task_group(struct task_struct *p, struct task_group **tg) { }
+#endif /* CONFIG_SCHED_AUTOGROUP */
kernel/sched_autogroup.c new file +161 -0

161 lines not on this page · raw message

kernel/sysctl.c +11 -0
Index: linux-2.6.36.git/kernel/sysctl.c
===================================================================
@@ -384,6 +384,17 @@ static struct ctl_table kern_table[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec,
 	},
+#ifdef CONFIG_SCHED_AUTOGROUP
+	{
+		.procname	= "sched_autogroup_enabled",
+		.data		= &sysctl_sched_autogroup_enabled,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= sched_autogroup_handler,
+		.extra1		= &zero,
+		.extra2		= &one,
+	},
+#endif
 #ifdef CONFIG_PROVE_LOCKING
 	{
 		.procname	= "prove_locking",
init/Kconfig +12 -0
Index: linux-2.6.36.git/init/Kconfig
===================================================================
@@ -652,6 +652,18 @@ config DEBUG_BLK_CGROUP
 
 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
 
Documentation/kernel-parameters.txt +2 -0
Index: linux-2.6.36.git/Documentation/kernel-parameters.txt
===================================================================
@@ -1610,6 +1610,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.
 
#32 lore raw
Quoted 9 lines · Mike Galbraith

I _finally_ got back to this yesterday, and implemented your suggestion, though with a couple minor variations. Putting the autogroup pointer in the signal struct didn't look right to me, so I plugged it into the task struct instead. I also didn't refcount taskgroups, wanted the patchlet to be as self-contained as possible, so refcounted the autogroup struct instead. I also left group movement on tty disassociation in place, but may nuke it.

The below has withstood an all night thrashing in my laptop with a PREEMPT_RT kernel, and looks kinda presentable to me, so...

The patch and the diffstat gives me warm fuzzy feelings:

Quoted 11 lines
---
 Documentation/kernel-parameters.txt |    2 
 drivers/char/tty_io.c               |    4 
 include/linux/sched.h               |   20 ++++
 init/Kconfig                        |   12 ++
 kernel/exit.c                       |    1 
 kernel/sched.c                      |   28 ++++--
 kernel/sched_autogroup.c            |  161 ++++++++++++++++++++++++++++++++++++
 kernel/sched_autogroup.h            |   10 ++
 kernel/sysctl.c                     |   11 ++
 9 files changed, 241 insertions(+), 8 deletions(-)

Very well contained, minimally invasive to anything else!

( Noticed only one very small detail: sched_autogroup.h has an illness of lack of
newlines which makes it a bit hard to read - but this is cured easily. )

I'll test and apply this patch to the scheduler tree, so if anyone has objections please holler now :-)

Linus, does this look OK to you too, can i add your Acked-by?

Thanks,

Ingo

#33 lore raw
In reply to Mike Galbraith
Quoted 8 lines · Mike Galbraith

I _finally_ got back to this yesterday, and implemented your suggestion, though with a couple minor variations.  Putting the autogroup pointer in the signal struct didn't look right to me, so I plugged it into the task struct instead.  I also didn't refcount taskgroups, wanted the patchlet to be as self-contained as possible, so refcounted the autogroup struct instead.  I also left group movement on tty disassociation in place, but may nuke it.

Ok, the patch looks fine, but I do have a few comments:

- the reason I suggested the signal struct was really that I thought
it would avoid extra (unnecessary) cost in thread creation/teardown.

Maybe I should have made that clear, but this seems to
unnecessarily do the whole atomic_inc/dec for each thread. That seems a bit sad.

That said, if not having to dereference ->signal simplifies the
scheduler interaction, I guess the extra atomic ref at thread creation/deletion is fine. So I don't think this is wrong, it's just something I wanted to bring up.

- You misspelled "detach". That just drives me wild. Please fix.

- What I _do_ think is wrong is how I think you're trying to be "too
precise". I think that's fundamentally wrong, because I think we should make it very clear that it's a heuristic. So I dislike seeing these functions: sched_autogroup_handler() - we shouldn't care about old state, sched_autogroup_detach() - even with the fixed spelling I don't really see why a tty hangup should cause the process to go back to the default group, for example.

IOW, I think you tried a bit _too_ hard to make it a 1:1 relationship with the tty. I don't think it needs to be. Just because a process loses its tty because of a hangup, I don't think that that should have any real implications for the auto-group scheduling. Or maybe it should, but that decision should be based on "does it help scheduling behavior" rather than on "it always matches the tty". See what I'm saying?

That said, I do love how the patch looks. I think this is absolutely the right thing to do. My issues are small details. I'd Ack it even in this form (well, as long as spelling is fixed, that really does rub me the wrong way), and the other things are more details that are about how I'm thinking about it rather than "you need to do it this way".

Linus

#34 lore raw
Quoted 15 lines · Linus Torvalds
Quoted 8 lines · Mike Galbraith

I _finally_ got back to this yesterday, and implemented your suggestion, though with a couple minor variations. Putting the autogroup pointer in the signal struct didn't look right to me, so I plugged it into the task struct instead. I also didn't refcount taskgroups, wanted the patchlet to be as self-contained as possible, so refcounted the autogroup struct instead. I also left group movement on tty disassociation in place, but may nuke it.

Ok, the patch looks fine, but I do have a few comments:

- the reason I suggested the signal struct was really that I thought
it would avoid extra (unnecessary) cost in thread creation/teardown.

Quoted 8 lines

Maybe I should have made that clear, but this seems to
unnecessarily do the whole atomic_inc/dec for each thread. That seems a bit sad.

That said, if not having to dereference ->signal simplifies the
scheduler interaction, I guess the extra atomic ref at thread creation/deletion is fine. So I don't think this is wrong, it's just something I wanted to bring up.

Ah, ok. Anything that cuts overhead is worth doing.

Quoted 1 line

- You misspelled "detach". That just drives me wild. Please fix.

(well, _somebody_ has to keep the speeling police occupied;)

Quoted 8 lines

- What I _do_ think is wrong is how I think you're trying to be "too
precise". I think that's fundamentally wrong, because I think we should make it very clear that it's a heuristic. So I dislike seeing these functions: sched_autogroup_handler() - we shouldn't care about old state, sched_autogroup_detach() - even with the fixed spelling I don't really see why a tty hangup should cause the process to go back to the default group, for example.

Quoted 7 lines

IOW, I think you tried a bit _too_ hard to make it a 1:1 relationship with the tty. I don't think it needs to be. Just because a process loses its tty because of a hangup, I don't think that that should have any real implications for the auto-group scheduling. Or maybe it should, but that decision should be based on "does it help scheduling behavior" rather than on "it always matches the tty". See what I'm saying?

Yeah, and it doesn't in the common case at least. The handler's classifier was because a 100% pinned hog would never obey the user's wishes, but I can whack it along with the hangup. Less is more in the scheduler.

Thanks for the comments.

-Mike

#35 lore raw
Quoted 8 lines · Mike Galbraith
Quoted 8 lines

 - the reason I suggested the signal struct was really that I thought it would avoid extra (unnecessary) cost in thread creation/teardown.

   Maybe I should have made that clear, but this seems to unnecessarily do the whole atomic_inc/dec for each thread. That seems a bit sad.

Quoted 6 lines
Quoted 4 lines

   That said, if not having to dereference ->signal simplifies the scheduler interaction, I guess the extra atomic ref at thread creation/deletion is fine. So I don't think this is wrong, it's just something I wanted to bring up.

Ah, ok.  Anything that cuts overhead is worth doing.

Well, it cuts both ways. Maybe your approach is simpler and avoids overhead at scheduling time. And "tsk->signal" may not be reliable due to races with exit etc, so it may well be that going through the signal struct could end up being a source of nasty races. I didn't look whether the scheduler already derefenced ->signal for some other reason, for example.

So your patch may well have done the exact right thing.

Linus

#36 lore raw
Quoted 7 lines · Linus Torvalds

Well, it cuts both ways. Maybe your approach is simpler and avoids overhead at scheduling time. And "tsk->signal" may not be reliable due to races with exit etc, so it may well be that going through the signal struct could end up being a source of nasty races. I didn't look whether the scheduler already derefenced ->signal for some other reason, for example.

Just in case, starting from 2.6.35 tsk->signal is reliable.

Oleg.

#37 lore raw
In reply to Mike Galbraith
Quoted 7 lines · Mike Galbraith

I _finally_ got back to this yesterday, and implemented your suggestion, though with a couple minor variations. Putting the autogroup pointer in the signal struct didn't look right to me, so I plugged it into the task struct instead. I also didn't refcount taskgroups, wanted the patchlet to be as self-contained as possible, so refcounted the autogroup struct instead. I also left group movement on tty disassociation in place, but may nuke it.

...

Quoted 10 lines

With taskset -c 3 make -j 10 running..

taskset -c 3 ./wakeup-latency& sleep 30;killall wakeup-latency

without:
maximum latency: 42963.2 µs
average latency: 9077.0 µs
missed timer events: 0

Quoted 4 lines

with:
maximum latency: 4160.7 µs
average latency: 149.4 µs
missed timer events: 0

Just to add some data; here are the results from my machine (AMD 4 cores) running a -j4 kernel build, while I browsed the web:

1) perf sched record sleep 30

without:
total_wakeups: 44306
avg_wakeup_latency (ns): 36784
min_wakeup_latency (ns): 0
max_wakeup_latency (ns): 9378852

with:
total_wakeups: 43836
avg_wakeup_latency (ns): 67607
min_wakeup_latency (ns): 0
max_wakeup_latency (ns): 8983036

2) perf record -a -e sched:sched_switch -e sched:sched_wakeup sleep 10

without:
total_wakeups: 13195
avg_wakeup_latency (ns): 48484
min_wakeup_latency (ns): 0
max_wakeup_latency (ns): 8722497

with:
total_wakeups: 14106
avg_wakeup_latency (ns): 92532
min_wakeup_latency (ns): 20
max_wakeup_latency (ns): 5642393

So the avg_wakeup_latency nearly doubled with your patch, while the max_wakeup_latency is lowered by a good amount.

Signature · 1 line
Markus
#38 lore raw
Quoted 25 lines · Markus Trippelsdorf

Just to add some data; here are the results from my machine (AMD 4 cores) running a -j4 kernel build, while I browsed the web:

1) perf sched record sleep 30

without:
total_wakeups: 44306
avg_wakeup_latency (ns): 36784
min_wakeup_latency (ns): 0
max_wakeup_latency (ns): 9378852

with:
total_wakeups: 43836
avg_wakeup_latency (ns): 67607
min_wakeup_latency (ns): 0
max_wakeup_latency (ns): 8983036

2) perf record -a -e sched:sched_switch -e sched:sched_wakeup sleep 10

without:
total_wakeups: 13195
avg_wakeup_latency (ns): 48484
min_wakeup_latency (ns): 0
max_wakeup_latency (ns): 8722497

Quoted 8 lines

with:
total_wakeups: 14106
avg_wakeup_latency (ns): 92532
min_wakeup_latency (ns): 20
max_wakeup_latency (ns): 5642393

So the avg_wakeup_latency nearly doubled with your patch, while the max_wakeup_latency is lowered by a good amount.

When you say with/without, does that mean enabled/disabled, or patched/virgin and/or cgroups/nocgroups?

-Mike

#39 lore raw
Quoted 33 lines · Mike Galbraith
Quoted 31 lines · Markus Trippelsdorf

Just to add some data; here are the results from my machine (AMD 4 cores) running a -j4 kernel build, while I browsed the web:

1) perf sched record sleep 30

without:
total_wakeups: 44306
avg_wakeup_latency (ns): 36784
min_wakeup_latency (ns): 0
max_wakeup_latency (ns): 9378852

with:
total_wakeups: 43836
avg_wakeup_latency (ns): 67607
min_wakeup_latency (ns): 0
max_wakeup_latency (ns): 8983036

2) perf record -a -e sched:sched_switch -e sched:sched_wakeup sleep 10

without:
total_wakeups: 13195
avg_wakeup_latency (ns): 48484
min_wakeup_latency (ns): 0
max_wakeup_latency (ns): 8722497

with:
total_wakeups: 14106
avg_wakeup_latency (ns): 92532
min_wakeup_latency (ns): 20
max_wakeup_latency (ns): 5642393

Quoted 5 lines
Quoted 2 lines

So the avg_wakeup_latency nearly doubled with your patch, while the max_wakeup_latency is lowered by a good amount.

When you say with/without, does that mean enabled/disabled, or patched/virgin and/or cgroups/nocgroups?

Patched/virgin and nocgroups

Signature · 1 line
Markus
#40 lore raw
Quoted 6 lines · Markus Trippelsdorf
Quoted 2 lines · Mike Galbraith

When you say with/without, does that mean enabled/disabled, or patched/virgin and/or cgroups/nocgroups?

Patched/virgin and nocgroups

Figures. As you can see, group scheduling is not wonderful for extreme switchers. Fortunately, most apps do a bit of work in between.

-Mike

#41 lore raw
In reply to Mike Galbraith

I didn't read this patch carefully (yet) but,

kernel/sched.c void sched_fork(struct task_struct *p, i · line 2579

Quoted 6 lines · Mike Galbraith
kernel/sched.c +1 -0
@@ -2569,6 +2576,7 @@ void sched_fork(struct task_struct *p, i
 	 * Silence PROVE_RCU.
 	 */
 	rcu_read_lock();
+	autogroup_fork(p);

Surely this doesn't need rcu.

But the real problem is that copy_process() can fail after that, and in this case we have the unbalanced kref_get().

kernel/exit.c repeat: · line 177

Quoted 6 lines
kernel/exit.c +2 -0
+++ linux-2.6.36.git/kernel/exit.c
@@ -174,6 +174,7 @@ repeat:
 	write_lock_irq(&tasklist_lock);
 	tracehook_finish_release_task(p);
 	__exit_signal(p);
+	sched_autogroup_exit(p);

This doesn't look right. Note that "p" can run/sleep after that (or in parallel), set_task_rq() can use the freed ->autogroup.

Btw, I can't apply this patch...

Oleg.

#42 lore raw
Quoted 11 lines · Oleg Nesterov

I didn't read this patch carefully (yet) but,

Review void sched_fork(struct task_struct *p, i · line 2579

Quoted 6 lines · Mike Galbraith
(quoted diff) +1 -0
@@ -2569,6 +2576,7 @@ void sched_fork(struct task_struct *p, i
 	 * Silence PROVE_RCU.
 	 */
 	rcu_read_lock();
+	autogroup_fork(p);

Surely this doesn't need rcu.

No, it was just a convenient spot.

Quoted 2 lines

But the real problem is that copy_process() can fail after that, and in this case we have the unbalanced kref_get().

Memory leak, will fix.

Quoted 9 lines

Review repeat: · line 177

Quoted 6 lines
(quoted diff) +2 -0
+++ linux-2.6.36.git/kernel/exit.c
@@ -174,6 +174,7 @@ repeat:
 	write_lock_irq(&tasklist_lock);
 	tracehook_finish_release_task(p);
 	__exit_signal(p);
+	sched_autogroup_exit(p);

This doesn't look right. Note that "p" can run/sleep after that (or in parallel), set_task_rq() can use the freed ->autogroup.

So avoiding refcounting rcu released task_group backfired. Crud.

Quoted 1 line

Btw, I can't apply this patch...

It depends on the patch below from Peter, or manual fixup.

Subject: sched, cgroup: Fixup broken cgroup movement
From: Peter Zijlstra <[email protected]>
Date: Fri Oct 15 15:24:15 CEST 2010

---
 include/linux/sched.h |    2 +-
 kernel/sched.c        |    8 ++++----
 kernel/sched_fair.c   |   25 +++++++++++++++++++------
 3 files changed, 24 insertions(+), 11 deletions(-)
kernel/sched.c +4 -4
Index: linux-2.6.36.git/kernel/sched.c
===================================================================
@@ -8297,12 +8297,12 @@ void sched_move_task(struct task_struct
 	if (unlikely(running))
 		tsk->sched_class->put_prev_task(rq, tsk);
 
-	set_task_rq(tsk, task_cpu(tsk));
-
 #ifdef CONFIG_FAIR_GROUP_SCHED
-	if (tsk->sched_class->moved_group)
-		tsk->sched_class->moved_group(tsk, on_rq);
+	if (tsk->sched_class->task_move_group)
+		tsk->sched_class->task_move_group(tsk, on_rq);
+	else
 #endif
+		set_task_rq(tsk, task_cpu(tsk));
 
 	if (unlikely(running))
 		tsk->sched_class->set_curr_task(rq);
include/linux/sched.h +1 -1
Index: linux-2.6.36.git/include/linux/sched.h
===================================================================
@@ -1072,7 +1072,7 @@ struct sched_class {
 					 struct task_struct *task);
 
 #ifdef CONFIG_FAIR_GROUP_SCHED
-	void (*moved_group) (struct task_struct *p, int on_rq);
+	void (*task_move_group) (struct task_struct *p, int on_rq);
 #endif
 };
 
kernel/sched_fair.c +19 -6
Index: linux-2.6.36.git/kernel/sched_fair.c
===================================================================
@@ -3824,13 +3824,26 @@ static void set_curr_task_fair(struct rq
 }
 
 #ifdef CONFIG_FAIR_GROUP_SCHED
-static void moved_group_fair(struct task_struct *p, int on_rq)
+static void task_move_group_fair(struct task_struct *p, int on_rq)
 {
-	struct cfs_rq *cfs_rq = task_cfs_rq(p);
-
-	update_curr(cfs_rq);
+	/*
+	 * If the task was not on the rq at the time of this cgroup movement
+	 * it must have been asleep, sleeping tasks keep their ->vruntime
+	 * absolute on their old rq until wakeup (needed for the fair sleeper
+	 * bonus in place_entity()).
+	 *
+	 * If it was on the rq, we've just 'preempted' it, which does convert
+	 * ->vruntime to a relative base.
+	 *
+	 * Make sure both cases convert their relative position when migrating
+	 * to another cgroup's rq. This does somewhat interfere with the
+	 * fair sleeper stuff for the first placement, but who cares.
+	 */
+	if (!on_rq)
+		p->se.vruntime -= cfs_rq_of(&p->se)->min_vruntime;
+	set_task_rq(p, task_cpu(p));
 	if (!on_rq)
-		place_entity(cfs_rq, &p->se, 1);
+		p->se.vruntime += cfs_rq_of(&p->se)->min_vruntime;
 }
 #endif
 
@@ -3882,7 +3895,7 @@ static const struct sched_class fair_sch
 	.get_rr_interval	= get_rr_interval_fair,
 
 #ifdef CONFIG_FAIR_GROUP_SCHED
-	.moved_group		= moved_group_fair,
+	.task_move_group	= task_move_group_fair,
 #endif
 };
 
#43 lore raw
Quoted 18 lines · Mike Galbraith
Quoted 2 lines · Oleg Nesterov

But the real problem is that copy_process() can fail after that, and in this case we have the unbalanced kref_get().

Memory leak, will fix.

Quoted 9 lines

Review repeat: · line 177

Quoted 6 lines

+++ linux-2.6.36.git/kernel/exit.c

(quoted diff) +1 -0
@@ -174,6 +174,7 @@ repeat:
 	write_lock_irq(&tasklist_lock);
 	tracehook_finish_release_task(p);
 	__exit_signal(p);
+	sched_autogroup_exit(p);

This doesn't look right. Note that "p" can run/sleep after that (or in parallel), set_task_rq() can use the freed ->autogroup.

Quoted 1 line

So avoiding refcounting rcu released task_group backfired. Crud.

Just in case, the lock order may be wrong. sched_autogroup_exit() takes task_group_lock under write_lock(tasklist), while sched_autogroup_handler() takes them in reverse order.

I am not sure, but perhaps this can be simpler? wake_up_new_task() does autogroup_fork(), and do_exit() does sched_autogroup_exit() before the last schedule. Possible?

Quoted 3 lines
Quoted 1 line

Btw, I can't apply this patch...

It depends on the patch below from Peter, or manual fixup.

Thanks. It also applies cleanly to 2.6.36.

Very basic question. Currently sched_autogroup_create_attach() has the only caller, __proc_set_tty(). It is a bit strange that signal->tty change is process-wide, but sched_autogroup_create_attach() move the single thread, the caller. What about other threads in this thread group? The same for proc_clear_tty().

Review

Quoted 12 lines
(quoted diff) +12 -0
+void sched_autogroup_create_attach(struct task_struct *p)
+{
+       autogroup_move_task(p, autogroup_create());
+
+       /*
+        * Correct freshly allocated group's refcount.
+ 	   * Move takes a reference on destination, but
+        * create already initialized refcount to 1.
+        */
+ 	if (p->autogroup != &autogroup_default)
+               autogroup_kref_put(p->autogroup);
+}

OK, but I don't understand "p->autogroup != &autogroup_default" check. This is true if autogroup_create() succeeds. Otherwise autogroup_create() does autogroup_kref_get(autogroup_default), doesn't this mean we need unconditional _put ?

And can't resist, minor cosmetic nit,

Review

Quoted 11 lines

static inline struct task_group *task_group(struct task_struct *p) {

(quoted diff) +4 -1
+       struct task_group *tg;
        struct cgroup_subsys_state *css;
 
        css = task_subsys_state_check(p, cpu_cgroup_subsys_id,
                        lockdep_is_held(&task_rq(p)->lock));
-       return container_of(css, struct task_group, css);
+       tg = container_of(css, struct task_group, css);
+
+       autogroup_task_group(p, &tg);

Fell free to ignore, but imho

	return autogroup_task_group(p, tg);

looks a bit better. Why autogroup_task_group() returns its result via pointer?

Oleg.

#44 lore raw
Quoted 19 lines · Oleg Nesterov
Quoted 18 lines · Mike Galbraith
Quoted 2 lines · Oleg Nesterov

But the real problem is that copy_process() can fail after that, and in this case we have the unbalanced kref_get().

Memory leak, will fix.

Quoted 9 lines

Review repeat: · line 177

Quoted 6 lines

+++ linux-2.6.36.git/kernel/exit.c

(quoted diff) +1 -0
@@ -174,6 +174,7 @@ repeat:
 	write_lock_irq(&tasklist_lock);
 	tracehook_finish_release_task(p);
 	__exit_signal(p);
+	sched_autogroup_exit(p);

This doesn't look right. Note that "p" can run/sleep after that (or in parallel), set_task_rq() can use the freed ->autogroup.

Quoted 5 lines
Quoted 1 line

So avoiding refcounting rcu released task_group backfired. Crud.

Just in case, the lock order may be wrong. sched_autogroup_exit() takes task_group_lock under write_lock(tasklist), while sched_autogroup_handler() takes them in reverse order.

Bug self destructs when global classifier goes away.

Quoted 3 lines

I am not sure, but perhaps this can be simpler? wake_up_new_task() does autogroup_fork(), and do_exit() does sched_autogroup_exit() before the last schedule. Possible?

That's what I was going to do. That said, I couldn't have had the problem if I'd tied final put directly to life of container, and am thinking I should do that instead when I go back to p->signal.

Quoted 5 lines

Very basic question. Currently sched_autogroup_create_attach() has the only caller, __proc_set_tty(). It is a bit strange that signal->tty change is process-wide, but sched_autogroup_create_attach() move the single thread, the caller. What about other threads in this thread group? The same for proc_clear_tty().

Yeah, I really should (will) move all on the spot, though it doesn't seem to matter in general practice, forks afterward land in the right bucket. With per tty or p->signal, migration will pick up stragglers lazily.. unless they're pinned.

Quoted 9 lines
Quoted 9 lines
(quoted diff) +9 -0
+void sched_autogroup_create_attach(struct task_struct *p)
+{
+       autogroup_move_task(p, autogroup_create());
+
+       /*
+        * Correct freshly allocated group's refcount.
+ 	   * Move takes a reference on destination, but
+        * create already initialized refcount to 1.
+        */
Quoted 8 lines

Review

Quoted 3 lines
(quoted diff) +3 -0
+ 	if (p->autogroup != &autogroup_default)
+               autogroup_kref_put(p->autogroup);
+}

OK, but I don't understand "p->autogroup != &autogroup_default" check. This is true if autogroup_create() succeeds. Otherwise autogroup_create() does autogroup_kref_get(autogroup_default), doesn't this mean we need unconditional _put ?

D'oh, target fixation :) Thanks.

Quoted 14 lines

And can't resist, minor cosmetic nit,

Review

Quoted 11 lines

static inline struct task_group *task_group(struct task_struct *p) {

(quoted diff) +4 -1
+       struct task_group *tg;
        struct cgroup_subsys_state *css;
 
        css = task_subsys_state_check(p, cpu_cgroup_subsys_id,
                        lockdep_is_held(&task_rq(p)->lock));
-       return container_of(css, struct task_group, css);
+       tg = container_of(css, struct task_group, css);
+
+       autogroup_task_group(p, &tg);
Quoted 6 lines

Fell free to ignore, but imho

	return autogroup_task_group(p, tg);

looks a bit better. Why autogroup_task_group() returns its result via pointer?

No particularly good reason, I'll do the cosmetic change.

Thanks,

-Mike

#45 lore raw
Quoted 20 lines · Mike Galbraith
Quoted 19 lines · Oleg Nesterov
Quoted 18 lines · Mike Galbraith
Quoted 2 lines · Oleg Nesterov

But the real problem is that copy_process() can fail after that, and in this case we have the unbalanced kref_get().

Memory leak, will fix.

Quoted 9 lines

Review repeat: · line 177

Quoted 6 lines

+++ linux-2.6.36.git/kernel/exit.c

(quoted diff) +1 -0
@@ -174,6 +174,7 @@ repeat:
 	write_lock_irq(&tasklist_lock);
 	tracehook_finish_release_task(p);
 	__exit_signal(p);
+	sched_autogroup_exit(p);

This doesn't look right. Note that "p" can run/sleep after that (or in parallel), set_task_rq() can use the freed ->autogroup.

Quoted 7 lines
Quoted 5 lines
Quoted 1 line

So avoiding refcounting rcu released task_group backfired. Crud.

Just in case, the lock order may be wrong. sched_autogroup_exit() takes task_group_lock under write_lock(tasklist), while sched_autogroup_handler() takes them in reverse order.

Bug self destructs when global classifier goes away.

I didn't nuke the handler, but did hide it under a debug option since it is useful for testing. If the user enables it, and turns autogroup off, imho off should means off NOW, so I stuck with it as is. I coded up a lazy (tick time check) move to handle pinned tasks not otherwise being moved, but that was too much for even my (lack of) taste to handle.

The locking should be fine as it was now, since autogroup_exit() isn't under the tasklist lock any more. (surprising i didn't hit any problems with this or use after free in rt kernel given how hard i beat on it)

Pondering adding some debug bits to identify autogroup tasks, maybe in /proc/N/cgroup or such.

Quoted 7 lines
Quoted 3 lines

I am not sure, but perhaps this can be simpler? wake_up_new_task() does autogroup_fork(), and do_exit() does sched_autogroup_exit() before the last schedule. Possible?

That's what I was going to do. That said, I couldn't have had the problem if I'd tied final put directly to life of container, and am thinking I should do that instead when I go back to p->signal.

I ended up tying it directly to p->signal's life, and beat on it with CONFIG_PREEMPT. I wanted to give it a thrashing in PREEMPT_RT, but when I snagged your signal patches, I apparently didn't snag quite enough, as the rt kernel with those patches is early boot doorstop.

Quoted 7 lines
Quoted 5 lines

Very basic question. Currently sched_autogroup_create_attach() has the only caller, __proc_set_tty(). It is a bit strange that signal->tty change is process-wide, but sched_autogroup_create_attach() move the single thread, the caller. What about other threads in this thread group? The same for proc_clear_tty().

Yeah, I really should (will) move all on the spot...

Did that, and the rest. This patch will apply to tip or .git.

patchlet:

A recurring complaint from CFS users is that parallel kbuild has a negative impact on desktop interactivity. This patch implements an idea from Linus, to automatically create task groups. This patch only implements Linus' per tty task group suggestion, and only for fair class tasks, but leaves the way open for enhancement.

Implementation: each task's signal struct contains an inherited pointer to a refcounted autogroup struct containing a task group pointer, the default for all tasks pointing to the init_task_group. When a task calls __proc_set_tty(), the process wide reference to the default group is dropped, a new task group is created, and the process is moved into the new task group. Children thereafter inherit this task group, and increase it's refcount. On exit, a reference to the current task group is dropped when the last reference to each signal struct is dropped. The task group is destroyed when the last signal struct referencing
it is freed. At runqueue selection time, IFF a task has no cgroup assignment,
it's current autogroup is used.

The feature is enabled from boot by default if CONFIG_SCHED_AUTOGROUP is selected, but can be disabled via the boot option noautogroup, and can be also be turned on/off on the fly if CONFIG_SCHED_AUTOGROUP is enabled via..
echo [01] > /proc/sys/kernel/sched_autogroup_enabled.
..which will automatically move tasks to/from the root task group.

Some numbers.

A 100% hog overhead measurement proggy pinned to the same CPU as a make -j10

About measurement proggy:
pert/sec = perturbations/sec
min/max/avg = scheduler service latencies in usecs
sum/s = time accrued by the competition per sample period (1 sec here) overhead = %CPU received by the competition per sample period

pert/s:       31 >40475.37us:        3 min:  0.37 max:48103.60 avg:29573.74 sum/s:916786us overhead:90.24%
pert/s:       23 >41237.70us:       12 min:  0.36 max:56010.39 avg:40187.01 sum/s:924301us overhead:91.99%
pert/s:       24 >42150.22us:       12 min:  8.86 max:61265.91 avg:39459.91 sum/s:947038us overhead:92.20%
pert/s:       26 >42344.91us:       11 min:  3.83 max:52029.60 avg:36164.70 sum/s:940282us overhead:91.12%
pert/s:       24 >44262.90us:       14 min:  5.05 max:82735.15 avg:40314.33 sum/s:967544us overhead:92.22%

Same load with this patch applied.

pert/s:      229 >5484.43us:       41 min:  0.15 max:12069.42 avg:2193.81 sum/s:502382us overhead:50.24%
pert/s:      222 >5652.28us:       43 min:  0.46 max:12077.31 avg:2248.56 sum/s:499181us overhead:49.92%
pert/s:      211 >5809.38us:       43 min:  0.16 max:12064.78 avg:2381.70 sum/s:502538us overhead:50.25%
pert/s:      223 >6147.92us:       43 min:  0.15 max:16107.46 avg:2282.17 sum/s:508925us overhead:50.49%
pert/s:      218 >6252.64us:       43 min:  0.16 max:12066.13 avg:2324.11 sum/s:506656us overhead:50.27%

Average service latency is an order of magnitude better with autogroup. (Imagine that pert were Xorg or whatnot instead)

Using Mathieu Desnoyers' wakeup-latency testcase:

With taskset -c 3 make -j 10 running..

taskset -c 3 ./wakeup-latency& sleep 30;killall wakeup-latency

without:
maximum latency: 42963.2 µs
average latency: 9077.0 µs
missed timer events: 0

with:
maximum latency: 4160.7 µs
average latency: 149.4 µs
missed timer events: 0

---
 Documentation/kernel-parameters.txt |    2 
 drivers/tty/tty_io.c                |    1 
 include/linux/sched.h               |   22 ++++
 init/Kconfig                        |   20 ++++
 kernel/fork.c                       |    5 -
 kernel/sched.c                      |   25 +++--
 kernel/sched_autogroup.c            |  170 ++++++++++++++++++++++++++++++++++++
 kernel/sched_autogroup.h            |   18 +++
 kernel/sysctl.c                     |   11 ++
 9 files changed, 265 insertions(+), 9 deletions(-)
include/linux/sched.h +22 -0
Index: linux-2.6/include/linux/sched.h
===================================================================
@@ -509,6 +509,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
@@ -576,6 +578,9 @@ 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.
@@ -1931,6 +1936,23 @@ int sched_rt_handler(struct ctl_table *t
 
 extern unsigned int sysctl_sched_compat_yield;
 
+#ifdef CONFIG_SCHED_AUTOGROUP
+#ifdef CONFIG_SCHED_AUTOGROUP_DEBUG
+extern unsigned int sysctl_sched_autogroup_enabled;
+int sched_autogroup_handler(struct ctl_table *table, int write,
+		void __user *buffer, size_t *lenp, loff_t *ppos);
+#endif
+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);
kernel/sched.c +17 -8
Index: linux-2.6/kernel/sched.c
===================================================================
@@ -78,6 +78,7 @@
 
 #include "sched_cpupri.h"
 #include "workqueue_sched.h"
+#include "sched_autogroup.h"
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/sched.h>
@@ -605,11 +606,14 @@ static inline int cpu_of(struct rq *rq)
  */
 static inline struct task_group *task_group(struct task_struct *p)
 {
+	struct task_group *tg;
 	struct cgroup_subsys_state *css;
 
 	css = task_subsys_state_check(p, cpu_cgroup_subsys_id,
 			lockdep_is_held(&task_rq(p)->lock));
-	return container_of(css, struct task_group, css);
+	tg = container_of(css, struct task_group, css);
+
+	return autogroup_task_group(p, tg);
 }
 
 /* Change a task's cfs_rq and parent entity if it moves across CPUs/groups */
@@ -2006,6 +2010,7 @@ static void sched_irq_time_avg_update(st
 #include "sched_idletask.c"
 #include "sched_fair.c"
 #include "sched_rt.c"
+#include "sched_autogroup.c"
 #include "sched_stoptask.c"
 #ifdef CONFIG_SCHED_DEBUG
 # include "sched_debug.c"
@@ -7979,7 +7984,7 @@ void __init sched_init(void)
 #ifdef CONFIG_CGROUP_SCHED
 	list_add(&init_task_group.list, &task_groups);
 	INIT_LIST_HEAD(&init_task_group.children);
-
+	autogroup_init(&init_task);
 #endif /* CONFIG_CGROUP_SCHED */
 
 #if defined CONFIG_FAIR_GROUP_SCHED && defined CONFIG_SMP
@@ -8509,15 +8514,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);
 
 	running = task_current(rq, tsk);
 	on_rq = tsk->se.on_rq;
@@ -8538,7 +8539,15 @@ void sched_move_task(struct task_struct
 		tsk->sched_class->set_curr_task(rq);
 	if (on_rq)
 		enqueue_task(rq, tsk, 0);
+}
 
+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);
 }
 #endif /* CONFIG_CGROUP_SCHED */
kernel/fork.c +4 -1
Index: linux-2.6/kernel/fork.c
===================================================================
@@ -174,8 +174,10 @@ static inline void free_signal_struct(st
 
 static inline void put_signal_struct(struct signal_struct *sig)
 {
-	if (atomic_dec_and_test(&sig->sigcnt))
+	if (atomic_dec_and_test(&sig->sigcnt)) {
+		sched_autogroup_exit(sig);
 		free_signal_struct(sig);
+	}
 }
 
 void __put_task_struct(struct task_struct *tsk)
@@ -904,6 +906,7 @@ static int copy_signal(unsigned long clo
 	posix_cpu_timers_init_group(sig);
 
 	tty_audit_fork(sig);
+	sched_autogroup_fork(sig);
 
 	sig->oom_adj = current->signal->oom_adj;
 	sig->oom_score_adj = current->signal->oom_score_adj;
drivers/tty/tty_io.c +1 -0
Index: linux-2.6/drivers/tty/tty_io.c
===================================================================
@@ -3160,6 +3160,7 @@ 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)
kernel/sched_autogroup.h new file +18 -0
Index: linux-2.6/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 */
kernel/sched_autogroup.c new file +169 -0

169 lines not on this page · raw message

kernel/sysctl.c +11 -0
Index: linux-2.6/kernel/sysctl.c
===================================================================
@@ -382,6 +382,17 @@ static struct ctl_table kern_table[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec,
 	},
+#ifdef CONFIG_SCHED_AUTOGROUP_DEBUG
+	{
+		.procname	= "sched_autogroup_enabled",
+		.data		= &sysctl_sched_autogroup_enabled,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= sched_autogroup_handler,
+		.extra1		= &zero,
+		.extra2		= &one,
+	},
+#endif
 #ifdef CONFIG_PROVE_LOCKING
 	{
 		.procname	= "prove_locking",
init/Kconfig +20 -0
Index: linux-2.6/init/Kconfig
===================================================================
@@ -728,6 +728,26 @@ config NET_NS
 
 endif # NAMESPACES
 
+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 SCHED_AUTOGROUP_DEBUG
+	bool "Enable Autogroup debugging"
+	depends on SCHED_AUTOGROUP
+	default n
+	help
+	  This option allows the user to enable/disable autogroup on the fly
+	  via echo [10] > /proc/sys/kernel/sched_autogroup_enabled.
+
 config MM_OWNER
 	bool
 
Documentation/kernel-parameters.txt +2 -0
Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================
@@ -1622,6 +1622,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.
 
#46 lore raw
Quoted 61 lines · Mike Galbraith
Quoted 20 lines · Mike Galbraith
Quoted 19 lines · Oleg Nesterov
Quoted 18 lines · Mike Galbraith
Quoted 2 lines · Oleg Nesterov

But the real problem is that copy_process() can fail after that, and in this case we have the unbalanced kref_get().

Memory leak, will fix.

Quoted 9 lines

Review repeat: · line 177

Quoted 6 lines

+++ linux-2.6.36.git/kernel/exit.c

(quoted diff) +1 -0
@@ -174,6 +174,7 @@ repeat:
 	write_lock_irq(&tasklist_lock);
 	tracehook_finish_release_task(p);
 	__exit_signal(p);
+	sched_autogroup_exit(p);

This doesn't look right. Note that "p" can run/sleep after that (or in parallel), set_task_rq() can use the freed ->autogroup.

Quoted 7 lines
Quoted 5 lines
Quoted 1 line

So avoiding refcounting rcu released task_group backfired. Crud.

Just in case, the lock order may be wrong. sched_autogroup_exit() takes task_group_lock under write_lock(tasklist), while sched_autogroup_handler() takes them in reverse order.

Bug self destructs when global classifier goes away.

I didn't nuke the handler, but did hide it under a debug option since it is useful for testing. If the user enables it, and turns autogroup off, imho off should means off NOW, so I stuck with it as is. I coded up a lazy (tick time check) move to handle pinned tasks not otherwise being moved, but that was too much for even my (lack of) taste to handle.

The locking should be fine as it was now, since autogroup_exit() isn't under the tasklist lock any more. (surprising i didn't hit any problems with this or use after free in rt kernel given how hard i beat on it)

Pondering adding some debug bits to identify autogroup tasks, maybe in /proc/N/cgroup or such.

Quoted 7 lines
Quoted 3 lines

I am not sure, but perhaps this can be simpler? wake_up_new_task() does autogroup_fork(), and do_exit() does sched_autogroup_exit() before the last schedule. Possible?

That's what I was going to do. That said, I couldn't have had the problem if I'd tied final put directly to life of container, and am thinking I should do that instead when I go back to p->signal.

I ended up tying it directly to p->signal's life, and beat on it with CONFIG_PREEMPT. I wanted to give it a thrashing in PREEMPT_RT, but when I snagged your signal patches, I apparently didn't snag quite enough, as the rt kernel with those patches is early boot doorstop.

Quoted 6 lines
Quoted 5 lines

Very basic question. Currently sched_autogroup_create_attach() has the only caller, __proc_set_tty(). It is a bit strange that signal->tty change is process-wide, but sched_autogroup_create_attach() move the single thread, the caller. What about other threads in this thread group? The same for proc_clear_tty().

Quoted 3 lines
Quoted 1 line

Yeah, I really should (will) move all on the spot...

Did that, and the rest. This patch will apply to tip or .git.

Unfortunately it won't. There's a missing "+" at line 507:

markus@arch linux % patch -p1 --dry-run < /home/markus/tty_autogroup2.patch patching file include/linux/sched.h
Hunk #3 succeeded at 1935 (offset -1 lines).
patching file kernel/sched.c
Hunk #2 succeeded at 607 (offset 1 line).
Hunk #3 succeeded at 2011 with fuzz 2 (offset 1 line).
Hunk #4 succeeded at 7959 (offset -25 lines).
Hunk #5 succeeded at 8489 (offset -25 lines).
Hunk #6 succeeded at 8514 (offset -25 lines).
patching file kernel/fork.c
patching file drivers/tty/tty_io.c
patching file kernel/sched_autogroup.h
patching file kernel/sched_autogroup.c
patch: **** malformed patch at line 507: Index: linux-2.6/kernel/sysctl.c

Signature · 1 line
Markus
#47 lore raw
Quoted 6 lines · Markus Trippelsdorf
Quoted 1 line · Mike Galbraith

Did that, and the rest. This patch will apply to tip or .git.

Unfortunately it won't. There's a missing "+" at line 507:

Quoted 14 lines

markus@arch linux % patch -p1 --dry-run < /home/markus/tty_autogroup2.patch patching file include/linux/sched.h
Hunk #3 succeeded at 1935 (offset -1 lines).
patching file kernel/sched.c
Hunk #2 succeeded at 607 (offset 1 line).
Hunk #3 succeeded at 2011 with fuzz 2 (offset 1 line).
Hunk #4 succeeded at 7959 (offset -25 lines).
Hunk #5 succeeded at 8489 (offset -25 lines).
Hunk #6 succeeded at 8514 (offset -25 lines).
patching file kernel/fork.c
patching file drivers/tty/tty_io.c
patching file kernel/sched_autogroup.h
patching file kernel/sched_autogroup.c
patch: **** malformed patch at line 507: Index: linux-2.6/kernel/sysctl.c

Oh well, yesterday ping (one of sister's laptop loving cats) opened _187_ screenshots while I was away from the keyboard. I'll blame any spelling errors on him too while I'm at it :)

Here's a fresh copy, no cats is sight.

A recurring complaint from CFS users is that parallel kbuild has a negative impact on desktop interactivity. This patch implements an idea from Linus, to automatically create task groups. This patch only implements Linus' per tty task group suggestion, and only for fair class tasks, but leaves the way open for enhancement.

Implementation: each task's signal struct contains an inherited pointer to a refcounted autogroup struct containing a task group pointer, the default for all tasks pointing to the init_task_group. When a task calls __proc_set_tty(), the process wide reference to the default group is dropped, a new task group is created, and the process is moved into the new task group. Children thereafter inherit this task group, and increase it's refcount. On exit, a reference to the current task group is dropped when the last reference to each signal struct is dropped. The task group is destroyed when the last signal struct referencing
it is freed. At runqueue selection time, IFF a task has no cgroup assignment,
it's current autogroup is used.

The feature is enabled from boot by default if CONFIG_SCHED_AUTOGROUP is selected, but can be disabled via the boot option noautogroup, and can be also be turned on/off on the fly via..
echo [01] > /proc/sys/kernel/sched_autogroup_enabled.
..which will automatically move tasks to/from the root task group.

Some numbers.

A 100% hog overhead measurement proggy pinned to the same CPU as a make -j10

About measurement proggy:
pert/sec = perturbations/sec
min/max/avg = scheduler service latencies in usecs
sum/s = time accrued by the competition per sample period (1 sec here) overhead = %CPU received by the competition per sample period

pert/s:       31 >40475.37us:        3 min:  0.37 max:48103.60 avg:29573.74 sum/s:916786us overhead:90.24%
pert/s:       23 >41237.70us:       12 min:  0.36 max:56010.39 avg:40187.01 sum/s:924301us overhead:91.99%
pert/s:       24 >42150.22us:       12 min:  8.86 max:61265.91 avg:39459.91 sum/s:947038us overhead:92.20%
pert/s:       26 >42344.91us:       11 min:  3.83 max:52029.60 avg:36164.70 sum/s:940282us overhead:91.12%
pert/s:       24 >44262.90us:       14 min:  5.05 max:82735.15 avg:40314.33 sum/s:967544us overhead:92.22%

Same load with this patch applied.

pert/s:      229 >5484.43us:       41 min:  0.15 max:12069.42 avg:2193.81 sum/s:502382us overhead:50.24%
pert/s:      222 >5652.28us:       43 min:  0.46 max:12077.31 avg:2248.56 sum/s:499181us overhead:49.92%
pert/s:      211 >5809.38us:       43 min:  0.16 max:12064.78 avg:2381.70 sum/s:502538us overhead:50.25%
pert/s:      223 >6147.92us:       43 min:  0.15 max:16107.46 avg:2282.17 sum/s:508925us overhead:50.49%
pert/s:      218 >6252.64us:       43 min:  0.16 max:12066.13 avg:2324.11 sum/s:506656us overhead:50.27%

Average service latency is an order of magnitude better with autogroup. (Imagine that pert were Xorg or whatnot instead)

Using Mathieu Desnoyers' wakeup-latency testcase:

With taskset -c 3 make -j 10 running..

taskset -c 3 ./wakeup-latency& sleep 30;killall wakeup-latency

without:
maximum latency: 42963.2 µs
average latency: 9077.0 µs
missed timer events: 0

with:
maximum latency: 4160.7 µs
average latency: 149.4 µs
missed timer events: 0

---
 Documentation/kernel-parameters.txt |    2 
 drivers/tty/tty_io.c                |    1 
 include/linux/sched.h               |   22 ++++
 init/Kconfig                        |   20 ++++
 kernel/fork.c                       |    5 -
 kernel/sched.c                      |   25 +++--
 kernel/sched_autogroup.c            |  170 ++++++++++++++++++++++++++++++++++++
 kernel/sched_autogroup.h            |   18 +++
 kernel/sysctl.c                     |   11 ++
 9 files changed, 265 insertions(+), 9 deletions(-)
include/linux/sched.h +22 -0
Index: linux-2.6/include/linux/sched.h
===================================================================
@@ -509,6 +509,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
@@ -576,6 +578,9 @@ 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.
@@ -1931,6 +1936,23 @@ int sched_rt_handler(struct ctl_table *t
 
 extern unsigned int sysctl_sched_compat_yield;
 
+#ifdef CONFIG_SCHED_AUTOGROUP
+#ifdef CONFIG_SCHED_AUTOGROUP_DEBUG
+extern unsigned int sysctl_sched_autogroup_enabled;
+int sched_autogroup_handler(struct ctl_table *table, int write,
+		void __user *buffer, size_t *lenp, loff_t *ppos);
+#endif
+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);
kernel/sched.c +17 -8
Index: linux-2.6/kernel/sched.c
===================================================================
@@ -78,6 +78,7 @@
 
 #include "sched_cpupri.h"
 #include "workqueue_sched.h"
+#include "sched_autogroup.h"
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/sched.h>
@@ -605,11 +606,14 @@ static inline int cpu_of(struct rq *rq)
  */
 static inline struct task_group *task_group(struct task_struct *p)
 {
+	struct task_group *tg;
 	struct cgroup_subsys_state *css;
 
 	css = task_subsys_state_check(p, cpu_cgroup_subsys_id,
 			lockdep_is_held(&task_rq(p)->lock));
-	return container_of(css, struct task_group, css);
+	tg = container_of(css, struct task_group, css);
+
+	return autogroup_task_group(p, tg);
 }
 
 /* Change a task's cfs_rq and parent entity if it moves across CPUs/groups */
@@ -2006,6 +2010,7 @@ static void sched_irq_time_avg_update(st
 #include "sched_idletask.c"
 #include "sched_fair.c"
 #include "sched_rt.c"
+#include "sched_autogroup.c"
 #include "sched_stoptask.c"
 #ifdef CONFIG_SCHED_DEBUG
 # include "sched_debug.c"
@@ -7979,7 +7984,7 @@ void __init sched_init(void)
 #ifdef CONFIG_CGROUP_SCHED
 	list_add(&init_task_group.list, &task_groups);
 	INIT_LIST_HEAD(&init_task_group.children);
-
+	autogroup_init(&init_task);
 #endif /* CONFIG_CGROUP_SCHED */
 
 #if defined CONFIG_FAIR_GROUP_SCHED && defined CONFIG_SMP
@@ -8509,15 +8514,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);
 
 	running = task_current(rq, tsk);
 	on_rq = tsk->se.on_rq;
@@ -8538,7 +8539,15 @@ void sched_move_task(struct task_struct
 		tsk->sched_class->set_curr_task(rq);
 	if (on_rq)
 		enqueue_task(rq, tsk, 0);
+}
 
+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);
 }
 #endif /* CONFIG_CGROUP_SCHED */
kernel/fork.c +4 -1
Index: linux-2.6/kernel/fork.c
===================================================================
@@ -174,8 +174,10 @@ static inline void free_signal_struct(st
 
 static inline void put_signal_struct(struct signal_struct *sig)
 {
-	if (atomic_dec_and_test(&sig->sigcnt))
+	if (atomic_dec_and_test(&sig->sigcnt)) {
+		sched_autogroup_exit(sig);
 		free_signal_struct(sig);
+	}
 }
 
 void __put_task_struct(struct task_struct *tsk)
@@ -904,6 +906,7 @@ static int copy_signal(unsigned long clo
 	posix_cpu_timers_init_group(sig);
 
 	tty_audit_fork(sig);
+	sched_autogroup_fork(sig);
 
 	sig->oom_adj = current->signal->oom_adj;
 	sig->oom_score_adj = current->signal->oom_score_adj;
drivers/tty/tty_io.c +1 -0
Index: linux-2.6/drivers/tty/tty_io.c
===================================================================
@@ -3160,6 +3160,7 @@ 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)
kernel/sched_autogroup.h new file +18 -0
Index: linux-2.6/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 */
kernel/sched_autogroup.c new file +170 -0

170 lines not on this page · raw message

kernel/sysctl.c +11 -0
Index: linux-2.6/kernel/sysctl.c
===================================================================
@@ -382,6 +382,17 @@ static struct ctl_table kern_table[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec,
 	},
+#ifdef CONFIG_SCHED_AUTOGROUP_DEBUG
+	{
+		.procname	= "sched_autogroup_enabled",
+		.data		= &sysctl_sched_autogroup_enabled,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= sched_autogroup_handler,
+		.extra1		= &zero,
+		.extra2		= &one,
+	},
+#endif
 #ifdef CONFIG_PROVE_LOCKING
 	{
 		.procname	= "prove_locking",
init/Kconfig +20 -0
Index: linux-2.6/init/Kconfig
===================================================================
@@ -728,6 +728,26 @@ config NET_NS
 
 endif # NAMESPACES
 
+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 SCHED_AUTOGROUP_DEBUG
+	bool "Enable Autogroup debugging"
+	depends on SCHED_AUTOGROUP
+	default n
+	help
+	  This option allows the user to enable/disable autogroup on the fly
+	  via echo [10] > /proc/sys/kernel/sched_autogroup_enabled.
+
 config MM_OWNER
 	bool
 
Documentation/kernel-parameters.txt +2 -0
Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================
@@ -1622,6 +1622,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.
 
#48 lore raw
Quoted 6 lines · Mike Galbraith

Oh well, yesterday ping (one of sister's laptop loving cats) opened _187_ screenshots while I was away from the keyboard.  I'll blame any spelling errors on him too while I'm at it :)

Here's a fresh copy, no cats is sight.

This catless version looks very good to me. Assuming testing and approval by the scheduler people, this has an enthusiastic "ack" from me.

Linus

#49 lore raw
Quoted 2 lines · Linus Torvalds

This catless version looks very good to me

Oh, btw, maybe one comment: is CONFIG_SCHED_AUTOGROUP_DEBUG even worth having as a config option? The only thing it enables is the small /proc interface, and I don't see any downside to just always having that if you have AUTOGROUP enabled in the first place.

It isn't like it's some traditional kernel debug feature that makes things slower or adds tons of debug output.

Linus

#50 lore raw
Quoted 5 lines · Linus Torvalds
Quoted 2 lines · Linus Torvalds

This catless version looks very good to me

Quoted 7 lines

Oh, btw, maybe one comment: is CONFIG_SCHED_AUTOGROUP_DEBUG even worth having as a config option? The only thing it enables is the small /proc interface, and I don't see any downside to just always having that if you have AUTOGROUP enabled in the first place.

It isn't like it's some traditional kernel debug feature that makes things slower or adds tons of debug output.

It also enables the sched_autogroup_handler, that you disliked seeing in the previous version.

Signature · 1 line
Markus
#51 lore raw
Quoted 7 lines · Markus Trippelsdorf
Quoted 3 lines · Linus Torvalds

It isn't like it's some traditional kernel debug feature that makes things slower or adds tons of debug output.

It also enables the sched_autogroup_handler, that you disliked seeing in the previous version.

Yes, but that one exists only to make things "exact". I don't really see why it exists. What's the point of doing the task movement, if the group information is just going to be ignored anyway?

IOW, my dislike of the sched_autogroup_handler is not about the debug option per se, it's about the same thing I said about tty hangups etc:
why do we care so deeply?

I think it would be perfectly acceptably to make the "enable" bit just decide whether or not to take that autogroup information into account for scheduling decision. Why do we care so deeply about having to move the groups around right _then_?

Maybe there is some really deep reason for actually having to do the reclassification and the sched_move_task() thing for each thread synchronously when disabling/enabling that thing. If so, I'm just not seeing it. Why can't we just let things be, and next time things get scheduled they'll move on their own?

So my objection was really about the same "why do we have to try so hard to keep the autogroups so 1:1 with the tty's"? It's just a heuristic, trying to be exact about it seems to miss the point.

Linus

#52 lore raw
Quoted 7 lines · Linus Torvalds
Quoted 5 lines · Markus Trippelsdorf
Quoted 3 lines · Linus Torvalds

It isn't like it's some traditional kernel debug feature that makes things slower or adds tons of debug output.

Quoted 6 lines
Quoted 2 lines

It also enables the sched_autogroup_handler, that you disliked seeing in the previous version.

Yes, but that one exists only to make things "exact". I don't really see why it exists. What's the point of doing the task movement, if the group information is just going to be ignored anyway?

Not only. pinned tasks would stay in their autogroup until somebody moved them to a cgroup. Them wandering back over time would be fine, and all but pinned tasks will.

-Mike

#53 lore raw
Quoted 4 lines · Mike Galbraith

Not only. pinned tasks would stay in their autogroup until somebody moved them to a cgroup.  Them wandering back over time would be fine, and all but pinned tasks will.

But why is a problem?

That's kind of my point. Why do we care about some special case that
(a) likely doesn't happen and (b) even if it happens, what's the problem with it happening?

Let me put it this way: the autogroup thing modifies the scheduler in some subtle ways, but it doesn't make any _semantic_ difference. And it's not like enabling/disabling autogroup scheduling is something critical to begin with. It's a heuristic.

THAT is why I think it's so silly to try to be so strict and walk over all processes while holding a couple of spinlocks.

Ok, so you disable autogroups after having used them, _and_ having done some really specific things, and some processes that used to be in a group may end up still scheduling in that group. Why care? It's not like random people can enable/disable autogroup scheduling and try to use this as a way to get unfair scheduling.

In fact, I'd go as far as saying that for the tty-based autogroup scheduling, if you want the autogroup policy to take effect, it would be perfectly ok if it really only affected the actual group _allocation_. So when you turn it on, old tty associations that existed before autogroup being turned on would not actually be in their own groups at all. And when you turn it off, existing tty groups would continue to exist, and you'd actually have to open a new window to get processes to no longer be part of any autogroup behavior.

See what I'm saying? I still think you're bending over backwards to be "careful" in ways that don't seem to make much sense to me.

Now, if there is some really fundamental reason why processes _have_ to be disassociated with the group when the autogroup policy changes, that would be a different issue. If the scheduler oopses when it hits a process that was in a tty autogroup but autogrouping has been turned off, _that_ would be a reason to say "recalculate everything when you disable/enable policy groups". But I don't think that's the case.

Linus

#54 lore raw
Quoted 3 lines · Linus Torvalds

THAT is why I think it's so silly to try to be so strict and walk over all processes while holding a couple of spinlocks.

Btw, let me say that I think the patch is great even with that thing in. It looks clean, the thing I'm complaining about is not a big deal, and it seems to perform very much as advertized. The difference with autogroup scheduling is very noticeable with a simple "make -j64" kernel compile.

So I really don't think it's a big deal. The sysctl handler isn't even complicated. But boy does it hurt my eyes to see a spinlock held around a "do_each_thread()". And I do get the feeling that the simplest way to fix it would be to just remove the code entirely, and just say that "enabling/disabling may be delayed for old processes with existing autogroups".

Linus

#55 lore raw
Quoted 12 lines · Linus Torvalds
Quoted 3 lines · Linus Torvalds

THAT is why I think it's so silly to try to be so strict and walk over all processes while holding a couple of spinlocks.

Btw, let me say that I think the patch is great even with that thing in. It looks clean, the thing I'm complaining about is not a big deal, and it seems to perform very much as advertized. The difference with autogroup scheduling is very noticeable with a simple "make -j64" kernel compile.

Quoted 6 lines

So I really don't think it's a big deal. The sysctl handler isn't even complicated. But boy does it hurt my eyes to see a spinlock held around a "do_each_thread()". And I do get the feeling that the simplest way to fix it would be to just remove the code entirely, and just say that "enabling/disabling may be delayed for old processes with existing autogroups".

Which is what I just did. If the oddball case isn't a big deal, the patch shrinks, which is a good thing. I just wanted to cover all bases.

Patchlet with handler whacked:

A recurring complaint from CFS users is that parallel kbuild has a negative impact on desktop interactivity. This patch implements an idea from Linus, to automatically create task groups. This patch only implements Linus' per tty task group suggestion, and only for fair class tasks, but leaves the way open for enhancement.

Implementation: each task's signal struct contains an inherited pointer to a refcounted autogroup struct containing a task group pointer, the default for all tasks pointing to the init_task_group. When a task calls __proc_set_tty(), the process wide reference to the default group is dropped, a new task group is created, and the process is moved into the new task group. Children thereafter inherit this task group, and increase it's refcount. On exit, a reference to the current task group is dropped when the last reference to each signal struct is dropped. The task group is destroyed when the last signal struct referencing
it is freed. At runqueue selection time, IFF a task has no cgroup assignment,
it's current autogroup is used.

The feature is enabled from boot by default if CONFIG_SCHED_AUTOGROUP is selected, but can be disabled via the boot option noautogroup, and can be also be turned on/off on the fly via..
echo [01] > /proc/sys/kernel/sched_autogroup_enabled.
..which will automatically move tasks to/from the root task group.

Some numbers.

A 100% hog overhead measurement proggy pinned to the same CPU as a make -j10

About measurement proggy:
pert/sec = perturbations/sec
min/max/avg = scheduler service latencies in usecs
sum/s = time accrued by the competition per sample period (1 sec here) overhead = %CPU received by the competition per sample period

pert/s:       31 >40475.37us:        3 min:  0.37 max:48103.60 avg:29573.74 sum/s:916786us overhead:90.24%
pert/s:       23 >41237.70us:       12 min:  0.36 max:56010.39 avg:40187.01 sum/s:924301us overhead:91.99%
pert/s:       24 >42150.22us:       12 min:  8.86 max:61265.91 avg:39459.91 sum/s:947038us overhead:92.20%
pert/s:       26 >42344.91us:       11 min:  3.83 max:52029.60 avg:36164.70 sum/s:940282us overhead:91.12%
pert/s:       24 >44262.90us:       14 min:  5.05 max:82735.15 avg:40314.33 sum/s:967544us overhead:92.22%

Same load with this patch applied.

pert/s:      229 >5484.43us:       41 min:  0.15 max:12069.42 avg:2193.81 sum/s:502382us overhead:50.24%
pert/s:      222 >5652.28us:       43 min:  0.46 max:12077.31 avg:2248.56 sum/s:499181us overhead:49.92%
pert/s:      211 >5809.38us:       43 min:  0.16 max:12064.78 avg:2381.70 sum/s:502538us overhead:50.25%
pert/s:      223 >6147.92us:       43 min:  0.15 max:16107.46 avg:2282.17 sum/s:508925us overhead:50.49%
pert/s:      218 >6252.64us:       43 min:  0.16 max:12066.13 avg:2324.11 sum/s:506656us overhead:50.27%

Average service latency is an order of magnitude better with autogroup. (Imagine that pert were Xorg or whatnot instead)

Using Mathieu Desnoyers' wakeup-latency testcase:

With taskset -c 3 make -j 10 running..

taskset -c 3 ./wakeup-latency& sleep 30;killall wakeup-latency

without:
maximum latency: 42963.2 µs
average latency: 9077.0 µs
missed timer events: 0

with:
maximum latency: 4160.7 µs
average latency: 149.4 µs
missed timer events: 0

---
 Documentation/kernel-parameters.txt |    2 
 drivers/tty/tty_io.c                |    1 
 include/linux/sched.h               |   19 ++++
 init/Kconfig                        |   12 +++
 kernel/fork.c                       |    5 +
 kernel/sched.c                      |   25 ++++--
 kernel/sched_autogroup.c            |  140 ++++++++++++++++++++++++++++++++++++
 kernel/sched_autogroup.h            |   18 ++++
 kernel/sysctl.c                     |   11 ++
 9 files changed, 224 insertions(+), 9 deletions(-)
include/linux/sched.h +19 -0
Index: linux-2.6/include/linux/sched.h
===================================================================
@@ -509,6 +509,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
@@ -576,6 +578,9 @@ 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.
@@ -1931,6 +1936,20 @@ 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);
kernel/sched.c +17 -8
Index: linux-2.6/kernel/sched.c
===================================================================
@@ -78,6 +78,7 @@
 
 #include "sched_cpupri.h"
 #include "workqueue_sched.h"
+#include "sched_autogroup.h"
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/sched.h>
@@ -605,11 +606,14 @@ static inline int cpu_of(struct rq *rq)
  */
 static inline struct task_group *task_group(struct task_struct *p)
 {
+	struct task_group *tg;
 	struct cgroup_subsys_state *css;
 
 	css = task_subsys_state_check(p, cpu_cgroup_subsys_id,
 			lockdep_is_held(&task_rq(p)->lock));
-	return container_of(css, struct task_group, css);
+	tg = container_of(css, struct task_group, css);
+
+	return autogroup_task_group(p, tg);
 }
 
 /* Change a task's cfs_rq and parent entity if it moves across CPUs/groups */
@@ -2006,6 +2010,7 @@ static void sched_irq_time_avg_update(st
 #include "sched_idletask.c"
 #include "sched_fair.c"
 #include "sched_rt.c"
+#include "sched_autogroup.c"
 #include "sched_stoptask.c"
 #ifdef CONFIG_SCHED_DEBUG
 # include "sched_debug.c"
@@ -7979,7 +7984,7 @@ void __init sched_init(void)
 #ifdef CONFIG_CGROUP_SCHED
 	list_add(&init_task_group.list, &task_groups);
 	INIT_LIST_HEAD(&init_task_group.children);
-
+	autogroup_init(&init_task);
 #endif /* CONFIG_CGROUP_SCHED */
 
 #if defined CONFIG_FAIR_GROUP_SCHED && defined CONFIG_SMP
@@ -8509,15 +8514,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);
 
 	running = task_current(rq, tsk);
 	on_rq = tsk->se.on_rq;
@@ -8538,7 +8539,15 @@ void sched_move_task(struct task_struct
 		tsk->sched_class->set_curr_task(rq);
 	if (on_rq)
 		enqueue_task(rq, tsk, 0);
+}
 
+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);
 }
 #endif /* CONFIG_CGROUP_SCHED */
kernel/fork.c +4 -1
Index: linux-2.6/kernel/fork.c
===================================================================
@@ -174,8 +174,10 @@ static inline void free_signal_struct(st
 
 static inline void put_signal_struct(struct signal_struct *sig)
 {
-	if (atomic_dec_and_test(&sig->sigcnt))
+	if (atomic_dec_and_test(&sig->sigcnt)) {
+		sched_autogroup_exit(sig);
 		free_signal_struct(sig);
+	}
 }
 
 void __put_task_struct(struct task_struct *tsk)
@@ -904,6 +906,7 @@ static int copy_signal(unsigned long clo
 	posix_cpu_timers_init_group(sig);
 
 	tty_audit_fork(sig);
+	sched_autogroup_fork(sig);
 
 	sig->oom_adj = current->signal->oom_adj;
 	sig->oom_score_adj = current->signal->oom_score_adj;
drivers/tty/tty_io.c +1 -0
Index: linux-2.6/drivers/tty/tty_io.c
===================================================================
@@ -3160,6 +3160,7 @@ 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)
kernel/sched_autogroup.h new file +18 -0
Index: linux-2.6/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 */
kernel/sched_autogroup.c new file +140 -0

140 lines not on this page · raw message

kernel/sysctl.c +11 -0
Index: linux-2.6/kernel/sysctl.c
===================================================================
@@ -382,6 +382,17 @@ static struct ctl_table kern_table[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec,
 	},
+#ifdef CONFIG_SCHED_AUTOGROUP
+	{
+		.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
 	{
 		.procname	= "prove_locking",
init/Kconfig +12 -0
Index: linux-2.6/init/Kconfig
===================================================================
@@ -728,6 +728,18 @@ config NET_NS
 
 endif # NAMESPACES
 
+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
 
Documentation/kernel-parameters.txt +2 -0
Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================
@@ -1622,6 +1622,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.
 
#56 lore raw
Quoted 3 lines · Mike Galbraith

Which is what I just did. If the oddball case isn't a big deal, the patch shrinks, which is a good thing. I just wanted to cover all bases.

Yeah. And I have to say that I'm (very happily) surprised by just how small that patch really ends up being, and how it's not intrusive or ugly either.

I'm also very happy with just what it does to interactive performance. Admittedly, my "testcase" is really trivial (reading email in a web-browser, scrolling around a bit, while doing a "make -j64" on the kernel at the same time), but it's a test-case that is very relevant for me. And it is a _huge_ improvement.

It's an improvement for things like smooth scrolling around, but what I found more interesting was how it seems to really make web pages load a lot faster. Maybe it shouldn't have been surprising, but I always associated that with network performance. But there's clearly enough of a CPU load when loading a new web page that if you have a load average of 50+ at the same time, you _will_ be starved for CPU in the loading process, and probably won't get all the http requests out quickly enough.

So I think this is firmly one of those "real improvement" patches. Good job. Group scheduling goes from "useful for some specific server loads" to "that's a killer feature".

Linus

#57 lore raw
Quoted 5 lines · Linus Torvalds

I'm also very happy with just what it does to interactive performance. Admittedly, my "testcase" is really trivial (reading email in a web-browser, scrolling around a bit, while doing a "make -j64" on the kernel at the same time), but it's a test-case that is very relevant for me. And it is a _huge_ improvement.

Next logical auto-step would be to try to subvert cfq. At a glance, io_context looks highly hackable for !CONFIG_CFQ_GROUP_IOSCHED case. The other case may get interesting for someone not very familiar with cfq innards, but I see a tempting looking subversion point.

-Mike

#58 lore raw
In reply to Mike Galbraith
Quoted 4 lines · Mike Galbraith
kernel/sched_autogroup.c new file +4 -0
+static inline struct task_group *
+autogroup_task_group(struct task_struct *p, struct task_group *tg)
+{
+       int enabled = ACCESS_ONCE(sysctl_sched_autogroup_enabled);

kernel/sched_autogroup.c line 75

Quoted 10 lines
kernel/sched_autogroup.c new file +10 -0
+
+       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;
+} 

That made me go wtf.. curious way of writing things.

I guess the usual way of writing that (which is admittedly a little more verbose) would be something like:

static bool
task_wants_autogroup(struct task_struct *tsk, struct task_group *tg)

{
	if (tg != &root_task_group)
		return false;

	if (tsk->sched_class != &fair_sched_class)
		return false;

	if (tsk->flags & PF_EXITING)
		return false;

	return true;
}

static inline struct task_group * autogroup_task_group(struct task_struct *tsk, struct task_group *tg)

{
	if (task_wants_autogroup(tsk, tg))
		return tsk->signal->autogroup->tg;

	return tg;
}
#59 lore raw
Quoted 37 lines · Peter Zijlstra
Quoted 4 lines · Mike Galbraith
(quoted diff) +4 -0
+static inline struct task_group *
+autogroup_task_group(struct task_struct *p, struct task_group *tg)
+{
+       int enabled = ACCESS_ONCE(sysctl_sched_autogroup_enabled);

Review

Quoted 10 lines
(quoted diff) +10 -0
+
+       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;
+} 

That made me go wtf.. curious way of writing things.

I guess the usual way of writing that (which is admittedly a little more verbose) would be something like:

static bool
task_wants_autogroup(struct task_struct *tsk, struct task_group *tg)

{
	if (tg != &root_task_group)
		return false;

	if (tsk->sched_class != &fair_sched_class)
		return false;

	if (tsk->flags & PF_EXITING)
		return false;

	return true;
}
Quoted 8 lines

static inline struct task_group * autogroup_task_group(struct task_struct *tsk, struct task_group *tg)

{
	if (task_wants_autogroup(tsk, tg))
		return tsk->signal->autogroup->tg;

	return tg;
}

That is a bit easier on the eye, modulo my hysterical attachment to "p".

Ok, so it's hopefully now ready to either take wing or go splat.

Baked:

A recurring complaint from CFS users is that parallel kbuild has a negative impact on desktop interactivity. This patch implements an idea from Linus, to automatically create task groups. This patch only implements Linus' per tty task group suggestion, and only for fair class tasks, but leaves the way open for enhancement.

Implementation: each task's signal struct contains an inherited pointer to a refcounted autogroup struct containing a task group pointer, the default for all tasks pointing to the init_task_group. When a task calls __proc_set_tty(), the process wide reference to the default group is dropped, a new task group is created, and the process is moved into the new task group. Children thereafter inherit this task group, and increase it's refcount. On exit, a reference to the current task group is dropped when the last reference to each signal struct is dropped. The task group is destroyed when the last signal struct referencing
it is freed. At runqueue selection time, IFF a task has no cgroup assignment,
it's current autogroup is used.

The feature is enabled from boot by default if CONFIG_SCHED_AUTOGROUP is selected, but can be disabled via the boot option noautogroup, and can be also be turned on/off on the fly via..
echo [01] > /proc/sys/kernel/sched_autogroup_enabled.
..which will automatically move tasks to/from the root task group.

Some numbers.

A 100% hog overhead measurement proggy pinned to the same CPU as a make -j10

About measurement proggy:
pert/sec = perturbations/sec
min/max/avg = scheduler service latencies in usecs
sum/s = time accrued by the competition per sample period (1 sec here) overhead = %CPU received by the competition per sample period

pert/s:       31 >40475.37us:        3 min:  0.37 max:48103.60 avg:29573.74 sum/s:916786us overhead:90.24%
pert/s:       23 >41237.70us:       12 min:  0.36 max:56010.39 avg:40187.01 sum/s:924301us overhead:91.99%
pert/s:       24 >42150.22us:       12 min:  8.86 max:61265.91 avg:39459.91 sum/s:947038us overhead:92.20%
pert/s:       26 >42344.91us:       11 min:  3.83 max:52029.60 avg:36164.70 sum/s:940282us overhead:91.12%
pert/s:       24 >44262.90us:       14 min:  5.05 max:82735.15 avg:40314.33 sum/s:967544us overhead:92.22%

Same load with this patch applied.

pert/s:      229 >5484.43us:       41 min:  0.15 max:12069.42 avg:2193.81 sum/s:502382us overhead:50.24%
pert/s:      222 >5652.28us:       43 min:  0.46 max:12077.31 avg:2248.56 sum/s:499181us overhead:49.92%
pert/s:      211 >5809.38us:       43 min:  0.16 max:12064.78 avg:2381.70 sum/s:502538us overhead:50.25%
pert/s:      223 >6147.92us:       43 min:  0.15 max:16107.46 avg:2282.17 sum/s:508925us overhead:50.49%
pert/s:      218 >6252.64us:       43 min:  0.16 max:12066.13 avg:2324.11 sum/s:506656us overhead:50.27%

Average service latency is an order of magnitude better with autogroup. (Imagine that pert were Xorg or whatnot instead)

Using Mathieu Desnoyers' wakeup-latency testcase:

With taskset -c 3 make -j 10 running..

taskset -c 3 ./wakeup-latency& sleep 30;killall wakeup-latency

without:
maximum latency: 42963.2 µs
average latency: 9077.0 µs
missed timer events: 0

with:
maximum latency: 4160.7 µs
average latency: 149.4 µs
missed timer events: 0

---
 Documentation/kernel-parameters.txt |    2 
 drivers/tty/tty_io.c                |    1 
 include/linux/sched.h               |   19 ++++
 init/Kconfig                        |   12 +++
 kernel/fork.c                       |    5 +
 kernel/sched.c                      |   25 ++++--
 kernel/sched_autogroup.c            |  140 ++++++++++++++++++++++++++++++++++++
 kernel/sched_autogroup.h            |   18 ++++
 kernel/sysctl.c                     |   11 ++
 9 files changed, 224 insertions(+), 9 deletions(-)
include/linux/sched.h +19 -0
Index: linux-2.6/include/linux/sched.h
===================================================================
@@ -509,6 +509,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
@@ -576,6 +578,9 @@ 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.
@@ -1931,6 +1936,20 @@ 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);
kernel/sched.c +17 -8
Index: linux-2.6/kernel/sched.c
===================================================================
@@ -78,6 +78,7 @@
 
 #include "sched_cpupri.h"
 #include "workqueue_sched.h"
+#include "sched_autogroup.h"
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/sched.h>
@@ -605,11 +606,14 @@ static inline int cpu_of(struct rq *rq)
  */
 static inline struct task_group *task_group(struct task_struct *p)
 {
+	struct task_group *tg;
 	struct cgroup_subsys_state *css;
 
 	css = task_subsys_state_check(p, cpu_cgroup_subsys_id,
 			lockdep_is_held(&task_rq(p)->lock));
-	return container_of(css, struct task_group, css);
+	tg = container_of(css, struct task_group, css);
+
+	return autogroup_task_group(p, tg);
 }
 
 /* Change a task's cfs_rq and parent entity if it moves across CPUs/groups */
@@ -2006,6 +2010,7 @@ static void sched_irq_time_avg_update(st
 #include "sched_idletask.c"
 #include "sched_fair.c"
 #include "sched_rt.c"
+#include "sched_autogroup.c"
 #include "sched_stoptask.c"
 #ifdef CONFIG_SCHED_DEBUG
 # include "sched_debug.c"
@@ -7979,7 +7984,7 @@ void __init sched_init(void)
 #ifdef CONFIG_CGROUP_SCHED
 	list_add(&init_task_group.list, &task_groups);
 	INIT_LIST_HEAD(&init_task_group.children);
-
+	autogroup_init(&init_task);
 #endif /* CONFIG_CGROUP_SCHED */
 
 #if defined CONFIG_FAIR_GROUP_SCHED && defined CONFIG_SMP
@@ -8509,15 +8514,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);
 
 	running = task_current(rq, tsk);
 	on_rq = tsk->se.on_rq;
@@ -8538,7 +8539,15 @@ void sched_move_task(struct task_struct
 		tsk->sched_class->set_curr_task(rq);
 	if (on_rq)
 		enqueue_task(rq, tsk, 0);
+}
 
+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);
 }
 #endif /* CONFIG_CGROUP_SCHED */
kernel/fork.c +4 -1
Index: linux-2.6/kernel/fork.c
===================================================================
@@ -174,8 +174,10 @@ static inline void free_signal_struct(st
 
 static inline void put_signal_struct(struct signal_struct *sig)
 {
-	if (atomic_dec_and_test(&sig->sigcnt))
+	if (atomic_dec_and_test(&sig->sigcnt)) {
+		sched_autogroup_exit(sig);
 		free_signal_struct(sig);
+	}
 }
 
 void __put_task_struct(struct task_struct *tsk)
@@ -904,6 +906,7 @@ static int copy_signal(unsigned long clo
 	posix_cpu_timers_init_group(sig);
 
 	tty_audit_fork(sig);
+	sched_autogroup_fork(sig);
 
 	sig->oom_adj = current->signal->oom_adj;
 	sig->oom_score_adj = current->signal->oom_score_adj;
drivers/tty/tty_io.c +1 -0
Index: linux-2.6/drivers/tty/tty_io.c
===================================================================
@@ -3160,6 +3160,7 @@ 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)
kernel/sched_autogroup.h new file +18 -0
Index: linux-2.6/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 */
kernel/sched_autogroup.c new file +140 -0

140 lines not on this page · raw message

kernel/sysctl.c +11 -0
Index: linux-2.6/kernel/sysctl.c
===================================================================
@@ -382,6 +382,17 @@ static struct ctl_table kern_table[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec,
 	},
+#ifdef CONFIG_SCHED_AUTOGROUP
+	{
+		.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
 	{
 		.procname	= "prove_locking",
init/Kconfig +12 -0
Index: linux-2.6/init/Kconfig
===================================================================
@@ -728,6 +728,18 @@ config NET_NS
 
 endif # NAMESPACES
 
+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
 
Documentation/kernel-parameters.txt +2 -0
Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================
@@ -1622,6 +1622,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.
 
#60 lore raw
Quoted 48 lines · Mike Galbraith
Quoted 37 lines · Peter Zijlstra
Quoted 4 lines · Mike Galbraith
(quoted diff) +4 -0
+static inline struct task_group *
+autogroup_task_group(struct task_struct *p, struct task_group *tg)
+{
+       int enabled = ACCESS_ONCE(sysctl_sched_autogroup_enabled);

Review

Quoted 10 lines
(quoted diff) +10 -0
+
+       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;
+} 

That made me go wtf.. curious way of writing things.

I guess the usual way of writing that (which is admittedly a little more verbose) would be something like:

static bool
task_wants_autogroup(struct task_struct *tsk, struct task_group *tg)

{
	if (tg != &root_task_group)
		return false;

	if (tsk->sched_class != &fair_sched_class)
		return false;

	if (tsk->flags & PF_EXITING)
		return false;

	return true;
}
Quoted 8 lines

static inline struct task_group * autogroup_task_group(struct task_struct *tsk, struct task_group *tg)

{
	if (task_wants_autogroup(tsk, tg))
		return tsk->signal->autogroup->tg;

	return tg;
}
Quoted 5 lines

That is a bit easier on the eye, modulo my hysterical attachment to "p".

Ok, so it's hopefully now ready to either take wing or go splat.

Baked:

The cat forgot to quilt refresh :)

A recurring complaint from CFS users is that parallel kbuild has a negative impact on desktop interactivity. This patch implements an idea from Linus, to automatically create task groups. This patch only implements Linus' per tty task group suggestion, and only for fair class tasks, but leaves the way open for enhancement.

Implementation: each task's signal struct contains an inherited pointer to a refcounted autogroup struct containing a task group pointer, the default for all tasks pointing to the init_task_group. When a task calls __proc_set_tty(), the process wide reference to the default group is dropped, a new task group is created, and the process is moved into the new task group. Children thereafter inherit this task group, and increase it's refcount. On exit, a reference to the current task group is dropped when the last reference to each signal struct is dropped. The task group is destroyed when the last signal struct referencing
it is freed. At runqueue selection time, IFF a task has no cgroup assignment,
it's current autogroup is used.

The feature is enabled from boot by default if CONFIG_SCHED_AUTOGROUP is selected, but can be disabled via the boot option noautogroup, and can be also be turned on/off on the fly via..
echo [01] > /proc/sys/kernel/sched_autogroup_enabled.
..which will automatically move tasks to/from the root task group.

Some numbers.

A 100% hog overhead measurement proggy pinned to the same CPU as a make -j10

About measurement proggy:
pert/sec = perturbations/sec
min/max/avg = scheduler service latencies in usecs
sum/s = time accrued by the competition per sample period (1 sec here) overhead = %CPU received by the competition per sample period

pert/s:       31 >40475.37us:        3 min:  0.37 max:48103.60 avg:29573.74 sum/s:916786us overhead:90.24%
pert/s:       23 >41237.70us:       12 min:  0.36 max:56010.39 avg:40187.01 sum/s:924301us overhead:91.99%
pert/s:       24 >42150.22us:       12 min:  8.86 max:61265.91 avg:39459.91 sum/s:947038us overhead:92.20%
pert/s:       26 >42344.91us:       11 min:  3.83 max:52029.60 avg:36164.70 sum/s:940282us overhead:91.12%
pert/s:       24 >44262.90us:       14 min:  5.05 max:82735.15 avg:40314.33 sum/s:967544us overhead:92.22%

Same load with this patch applied.

pert/s:      229 >5484.43us:       41 min:  0.15 max:12069.42 avg:2193.81 sum/s:502382us overhead:50.24%
pert/s:      222 >5652.28us:       43 min:  0.46 max:12077.31 avg:2248.56 sum/s:499181us overhead:49.92%
pert/s:      211 >5809.38us:       43 min:  0.16 max:12064.78 avg:2381.70 sum/s:502538us overhead:50.25%
pert/s:      223 >6147.92us:       43 min:  0.15 max:16107.46 avg:2282.17 sum/s:508925us overhead:50.49%
pert/s:      218 >6252.64us:       43 min:  0.16 max:12066.13 avg:2324.11 sum/s:506656us overhead:50.27%

Average service latency is an order of magnitude better with autogroup. (Imagine that pert were Xorg or whatnot instead)

Using Mathieu Desnoyers' wakeup-latency testcase:

With taskset -c 3 make -j 10 running..

taskset -c 3 ./wakeup-latency& sleep 30;killall wakeup-latency

without:
maximum latency: 42963.2 µs
average latency: 9077.0 µs
missed timer events: 0

with:
maximum latency: 4160.7 µs
average latency: 149.4 µs
missed timer events: 0

---
 Documentation/kernel-parameters.txt |    2 
 drivers/tty/tty_io.c                |    1 
 include/linux/sched.h               |   19 ++++
 init/Kconfig                        |   12 ++
 kernel/fork.c                       |    5 -
 kernel/sched.c                      |   25 ++++-
 kernel/sched_autogroup.c            |  151 ++++++++++++++++++++++++++++++++++++
 kernel/sched_autogroup.h            |   18 ++++
 kernel/sysctl.c                     |   11 ++
 9 files changed, 235 insertions(+), 9 deletions(-)
include/linux/sched.h +19 -0
Index: linux-2.6/include/linux/sched.h
===================================================================
@@ -509,6 +509,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
@@ -576,6 +578,9 @@ 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.
@@ -1931,6 +1936,20 @@ 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);
kernel/sched.c +17 -8
Index: linux-2.6/kernel/sched.c
===================================================================
@@ -78,6 +78,7 @@
 
 #include "sched_cpupri.h"
 #include "workqueue_sched.h"
+#include "sched_autogroup.h"
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/sched.h>
@@ -605,11 +606,14 @@ static inline int cpu_of(struct rq *rq)
  */
 static inline struct task_group *task_group(struct task_struct *p)
 {
+	struct task_group *tg;
 	struct cgroup_subsys_state *css;
 
 	css = task_subsys_state_check(p, cpu_cgroup_subsys_id,
 			lockdep_is_held(&task_rq(p)->lock));
-	return container_of(css, struct task_group, css);
+	tg = container_of(css, struct task_group, css);
+
+	return autogroup_task_group(p, tg);
 }
 
 /* Change a task's cfs_rq and parent entity if it moves across CPUs/groups */
@@ -2006,6 +2010,7 @@ static void sched_irq_time_avg_update(st
 #include "sched_idletask.c"
 #include "sched_fair.c"
 #include "sched_rt.c"
+#include "sched_autogroup.c"
 #include "sched_stoptask.c"
 #ifdef CONFIG_SCHED_DEBUG
 # include "sched_debug.c"
@@ -7979,7 +7984,7 @@ void __init sched_init(void)
 #ifdef CONFIG_CGROUP_SCHED
 	list_add(&init_task_group.list, &task_groups);
 	INIT_LIST_HEAD(&init_task_group.children);
-
+	autogroup_init(&init_task);
 #endif /* CONFIG_CGROUP_SCHED */
 
 #if defined CONFIG_FAIR_GROUP_SCHED && defined CONFIG_SMP
@@ -8509,15 +8514,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);
 
 	running = task_current(rq, tsk);
 	on_rq = tsk->se.on_rq;
@@ -8538,7 +8539,15 @@ void sched_move_task(struct task_struct
 		tsk->sched_class->set_curr_task(rq);
 	if (on_rq)
 		enqueue_task(rq, tsk, 0);
+}
 
+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);
 }
 #endif /* CONFIG_CGROUP_SCHED */
kernel/fork.c +4 -1
Index: linux-2.6/kernel/fork.c
===================================================================
@@ -174,8 +174,10 @@ static inline void free_signal_struct(st
 
 static inline void put_signal_struct(struct signal_struct *sig)
 {
-	if (atomic_dec_and_test(&sig->sigcnt))
+	if (atomic_dec_and_test(&sig->sigcnt)) {
+		sched_autogroup_exit(sig);
 		free_signal_struct(sig);
+	}
 }
 
 void __put_task_struct(struct task_struct *tsk)
@@ -904,6 +906,7 @@ static int copy_signal(unsigned long clo
 	posix_cpu_timers_init_group(sig);
 
 	tty_audit_fork(sig);
+	sched_autogroup_fork(sig);
 
 	sig->oom_adj = current->signal->oom_adj;
 	sig->oom_score_adj = current->signal->oom_score_adj;
drivers/tty/tty_io.c +1 -0
Index: linux-2.6/drivers/tty/tty_io.c
===================================================================
@@ -3160,6 +3160,7 @@ 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)
kernel/sched_autogroup.h new file +18 -0
Index: linux-2.6/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 */
kernel/sched_autogroup.c new file +151 -0

151 lines not on this page · raw message

kernel/sysctl.c +11 -0
Index: linux-2.6/kernel/sysctl.c
===================================================================
@@ -382,6 +382,17 @@ static struct ctl_table kern_table[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec,
 	},
+#ifdef CONFIG_SCHED_AUTOGROUP
+	{
+		.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
 	{
 		.procname	= "prove_locking",
init/Kconfig +12 -0
Index: linux-2.6/init/Kconfig
===================================================================
@@ -728,6 +728,18 @@ config NET_NS
 
 endif # NAMESPACES
 
+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
 
Documentation/kernel-parameters.txt +2 -0
Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================
@@ -1622,6 +1622,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.
 
#61 lore raw

I continue to play the advocatus diaboli ;)

kernel/sched_autogroup.c line 72

Quoted 12 lines · Mike Galbraith
kernel/sched_autogroup.c new file +11 -0
+static inline bool
+task_wants_autogroup(struct task_struct *p, struct task_group *tg)
+{
+	if (tg != &root_task_group)
+		return false;
+
+	if (p->sched_class != &fair_sched_class)
+		return false;
+
+	if (p->flags & PF_EXITING)
+		return false;

Hmm, why? Perhaps PF_EXITING was needed in the previous version to avoid the race with release_task(). But now it is always safe to use signal->autogroup.

And the exiting task can do a lot before it disappears, probably we shouldn't ignore ->autogroup.

Quoted 14 lines
kernel/sched_autogroup.c new file +14 -0
+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;
+	}

kernel/sched_autogroup.c line 111

Quoted 10 lines
kernel/sched_autogroup.c new file +10 -0
+
+	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();

Not sure I understand why do we need rq->lock...

It can't protect the change of signal->autogroup, multiple callers can use different rq's.

However. Currently the only callers holds ->siglock, so we are safe. Perhaps we should just document that autogroup_move_group() needs ->siglock.

This also mean the patch can be simplified even more, __sched_move_task() is not needed.

kernel/sched_autogroup.c line 136

Quoted 4 lines
kernel/sched_autogroup.c new file +4 -0
+void sched_autogroup_fork(struct signal_struct *sig)
+{
+	sig->autogroup = autogroup_kref_get(current->signal->autogroup);
+}

Well, in theory this can race with another thread doing autogroup_move_group(). We can read the old ->autogroup, and then use it after it was already freed.

Probably this needs ->siglock too.

Oleg.

#62 lore raw
Quoted 1 line · Oleg Nesterov

I continue to play the advocatus diaboli ;)

Much appreciated.

Quoted 17 lines · Mike Galbraith

Review

Quoted 12 lines
(quoted diff) +11 -0
+static inline bool
+task_wants_autogroup(struct task_struct *p, struct task_group *tg)
+{
+	if (tg != &root_task_group)
+		return false;
+
+	if (p->sched_class != &fair_sched_class)
+		return false;
+
+	if (p->flags & PF_EXITING)
+		return false;

Hmm, why? Perhaps PF_EXITING was needed in the previous version to avoid the race with release_task(). But now it is always safe to use signal->autogroup.

That came into existence when I stress tested previous version in PREEMPT_RT (boom). I see no good reason to bother an exiting task though, so would prefer to leave it as is.

Quoted 2 lines

And the exiting task can do a lot before it disappears, probably we shouldn't ignore ->autogroup.

I doubt it would add value.

Quoted 1 line

Not sure I understand why do we need rq->lock...

Indeed, should have been whacked. Now gone.

Quoted 2 lines

It can't protect the change of signal->autogroup, multiple callers can use different rq's.

Guaranteed live ->autogroup should be good enough for heuristic use, and had better be so. Having to take ->siglock in the fast path would kill using ->signal.

Quoted 3 lines

However. Currently the only callers holds ->siglock, so we are safe. Perhaps we should just document that autogroup_move_group() needs ->siglock.

Done.

Quoted 2 lines

This also mean the patch can be simplified even more, __sched_move_task() is not needed.

(shrinkage:)

Quoted 9 lines

Review

Quoted 4 lines
(quoted diff) +4 -0
+void sched_autogroup_fork(struct signal_struct *sig)
+{
+	sig->autogroup = autogroup_kref_get(current->signal->autogroup);
+}

Well, in theory this can race with another thread doing autogroup_move_group(). We can read the old ->autogroup, and then use it after it was already freed.

Probably this needs ->siglock too.

Another landmine. Done.

A recurring complaint from CFS users is that parallel kbuild has a negative impact on desktop interactivity. This patch implements an idea from Linus, to automatically create task groups. This patch only implements Linus' per tty task group suggestion, and only for fair class tasks, but leaves the way open for enhancement.

Implementation: each task's signal struct contains an inherited pointer to a refcounted autogroup struct containing a task group pointer, the default for all tasks pointing to the init_task_group. When a task calls __proc_set_tty(), the process wide reference to the default group is dropped, a new task group is created, and the process is moved into the new task group. Children thereafter inherit this task group, and increase it's refcount. On exit, a reference to the current task group is dropped when the last reference to each signal struct is dropped. The task group is destroyed when the last signal struct referencing
it is freed. At runqueue selection time, IFF a task has no cgroup assignment,
it's current autogroup is used.

The feature is enabled from boot by default if CONFIG_SCHED_AUTOGROUP is selected, but can be disabled via the boot option noautogroup, and can be also be turned on/off on the fly via..
echo [01] > /proc/sys/kernel/sched_autogroup_enabled.
..which will automatically move tasks to/from the root task group.

Some numbers.

A 100% hog overhead measurement proggy pinned to the same CPU as a make -j10

About measurement proggy:
pert/sec = perturbations/sec
min/max/avg = scheduler service latencies in usecs
sum/s = time accrued by the competition per sample period (1 sec here) overhead = %CPU received by the competition per sample period

pert/s:       31 >40475.37us:        3 min:  0.37 max:48103.60 avg:29573.74 sum/s:916786us overhead:90.24%
pert/s:       23 >41237.70us:       12 min:  0.36 max:56010.39 avg:40187.01 sum/s:924301us overhead:91.99%
pert/s:       24 >42150.22us:       12 min:  8.86 max:61265.91 avg:39459.91 sum/s:947038us overhead:92.20%
pert/s:       26 >42344.91us:       11 min:  3.83 max:52029.60 avg:36164.70 sum/s:940282us overhead:91.12%
pert/s:       24 >44262.90us:       14 min:  5.05 max:82735.15 avg:40314.33 sum/s:967544us overhead:92.22%

Same load with this patch applied.

pert/s:      229 >5484.43us:       41 min:  0.15 max:12069.42 avg:2193.81 sum/s:502382us overhead:50.24%
pert/s:      222 >5652.28us:       43 min:  0.46 max:12077.31 avg:2248.56 sum/s:499181us overhead:49.92%
pert/s:      211 >5809.38us:       43 min:  0.16 max:12064.78 avg:2381.70 sum/s:502538us overhead:50.25%
pert/s:      223 >6147.92us:       43 min:  0.15 max:16107.46 avg:2282.17 sum/s:508925us overhead:50.49%
pert/s:      218 >6252.64us:       43 min:  0.16 max:12066.13 avg:2324.11 sum/s:506656us overhead:50.27%

Average service latency is an order of magnitude better with autogroup. (Imagine that pert were Xorg or whatnot instead)

Using Mathieu Desnoyers' wakeup-latency testcase:

With taskset -c 3 make -j 10 running..

taskset -c 3 ./wakeup-latency& sleep 30;killall wakeup-latency

without:
maximum latency: 42963.2 µs
average latency: 9077.0 µs
missed timer events: 0

with:
maximum latency: 4160.7 µs
average latency: 149.4 µs
missed timer events: 0

---
 Documentation/kernel-parameters.txt |    2 
 drivers/tty/tty_io.c                |    1 
 include/linux/sched.h               |   19 ++++
 init/Kconfig                        |   12 ++
 kernel/fork.c                       |    5 -
 kernel/sched.c                      |    9 +-
 kernel/sched_autogroup.c            |  150 ++++++++++++++++++++++++++++++++++++
 kernel/sched_autogroup.h            |   16 +++
 kernel/sysctl.c                     |   11 ++
 9 files changed, 222 insertions(+), 3 deletions(-)
include/linux/sched.h +19 -0
Index: linux-2.6/include/linux/sched.h
===================================================================
@@ -509,6 +509,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
@@ -576,6 +578,9 @@ 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.
@@ -1931,6 +1936,20 @@ 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);
kernel/sched.c +7 -2
Index: linux-2.6/kernel/sched.c
===================================================================
@@ -78,6 +78,7 @@
 
 #include "sched_cpupri.h"
 #include "workqueue_sched.h"
+#include "sched_autogroup.h"
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/sched.h>
@@ -605,11 +606,14 @@ static inline int cpu_of(struct rq *rq)
  */
 static inline struct task_group *task_group(struct task_struct *p)
 {
+	struct task_group *tg;
 	struct cgroup_subsys_state *css;
 
 	css = task_subsys_state_check(p, cpu_cgroup_subsys_id,
 			lockdep_is_held(&task_rq(p)->lock));
-	return container_of(css, struct task_group, css);
+	tg = container_of(css, struct task_group, css);
+
+	return autogroup_task_group(p, tg);
 }
 
 /* Change a task's cfs_rq and parent entity if it moves across CPUs/groups */
@@ -2006,6 +2010,7 @@ static void sched_irq_time_avg_update(st
 #include "sched_idletask.c"
 #include "sched_fair.c"
 #include "sched_rt.c"
+#include "sched_autogroup.c"
 #include "sched_stoptask.c"
 #ifdef CONFIG_SCHED_DEBUG
 # include "sched_debug.c"
@@ -7979,7 +7984,7 @@ void __init sched_init(void)
 #ifdef CONFIG_CGROUP_SCHED
 	list_add(&init_task_group.list, &task_groups);
 	INIT_LIST_HEAD(&init_task_group.children);
-
+	autogroup_init(&init_task);
 #endif /* CONFIG_CGROUP_SCHED */
 
 #if defined CONFIG_FAIR_GROUP_SCHED && defined CONFIG_SMP
kernel/fork.c +4 -1
Index: linux-2.6/kernel/fork.c
===================================================================
@@ -174,8 +174,10 @@ static inline void free_signal_struct(st
 
 static inline void put_signal_struct(struct signal_struct *sig)
 {
-	if (atomic_dec_and_test(&sig->sigcnt))
+	if (atomic_dec_and_test(&sig->sigcnt)) {
+		sched_autogroup_exit(sig);
 		free_signal_struct(sig);
+	}
 }
 
 void __put_task_struct(struct task_struct *tsk)
@@ -904,6 +906,7 @@ static int copy_signal(unsigned long clo
 	posix_cpu_timers_init_group(sig);
 
 	tty_audit_fork(sig);
+	sched_autogroup_fork(sig);
 
 	sig->oom_adj = current->signal->oom_adj;
 	sig->oom_score_adj = current->signal->oom_score_adj;
drivers/tty/tty_io.c +1 -0
Index: linux-2.6/drivers/tty/tty_io.c
===================================================================
@@ -3160,6 +3160,7 @@ 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)
kernel/sched_autogroup.h new file +16 -0
Index: linux-2.6/kernel/sched_autogroup.h
===================================================================
@@ -0,0 +1,16 @@
+#ifdef CONFIG_SCHED_AUTOGROUP
+
+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 */
kernel/sched_autogroup.c new file +150 -0

150 lines not on this page · raw message

kernel/sysctl.c +11 -0
Index: linux-2.6/kernel/sysctl.c
===================================================================
@@ -382,6 +382,17 @@ static struct ctl_table kern_table[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec,
 	},
+#ifdef CONFIG_SCHED_AUTOGROUP
+	{
+		.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
 	{
 		.procname	= "prove_locking",
init/Kconfig +12 -0
Index: linux-2.6/init/Kconfig
===================================================================
@@ -728,6 +728,18 @@ config NET_NS
 
 endif # NAMESPACES
 
+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
 
Documentation/kernel-parameters.txt +2 -0
Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================
@@ -1622,6 +1622,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.
 
#63 lore raw
Quoted 5 lines · Mike Galbraith
Quoted 3 lines

However. Currently the only callers holds ->siglock, so we are safe. Perhaps we should just document that autogroup_move_group() needs ->siglock.

Done.

lockdep_assert_held() is a good way to document these things.

#64 lore raw
In reply to Mike Galbraith

On Mon, Nov 15, 2010 at 02:25:50PM -0700, Mike Galbraith wrote:

[..]

Quoted 7 lines

A recurring complaint from CFS users is that parallel kbuild has a negative impact on desktop interactivity. This patch implements an idea from Linus, to automatically create task groups. This patch only implements Linus' per tty task group suggestion, and only for fair class tasks, but leaves the way open for enhancement.

Quoted 10 lines

Implementation: each task's signal struct contains an inherited pointer to a refcounted autogroup struct containing a task group pointer, the default for all tasks pointing to the init_task_group. When a task calls __proc_set_tty(), the process wide reference to the default group is dropped, a new task group is created, and the process is moved into the new task group. Children thereafter inherit this task group, and increase it's refcount. On exit, a reference to the current task group is dropped when the last reference to each signal struct is dropped. The task group is destroyed when the last signal struct referencing
it is freed. At runqueue selection time, IFF a task has no cgroup assignment,
it's current autogroup is used.

Mike,

IIUC, this automatically created task group is invisible to user space? I mean generally there is a task group associated with a cgroup and user space tools can walk through cgroup hierarchy to figure out how system is configured. Will that be possible with this patch.

I am wondering what will happen to things like some kind of per cgroup stats. For example block controller keeps track of number of sectors transferred per cgroup. Hence this information will not be available for these internal task groups?

Looks like everybody likes the idea but let me still ask the following question.

Should this kind of thing be done in user space? I mean what we are essentially doing providing isolation between two groups. That's why this cgroup infrastructure is in place. Just that currently how cgroups are created is fully depends on user space and kernel does not create cgroups of its own by default (ecept root cgroup).

I think systemd does something similar in the sense every system service it puts in a cgroup of its own on system startup.

libcgroup daemon has the facility to listen for kernel events (through netlink socket), and then put newly created tasks in cgroups as per the user spcefied rules in a config file. For example, if one wants isolation between tasks of two user ids, one can just write a rule and once the user logs in, its login session will be automatically placed in right cgroup. Hence one will be able to achieve isolation between two users. I think now it also has rules for classifying executables based on names/paths. So one can put "firefox" in one cgroup and say "make -j64" in a separate cgroup and provide isolation between two applications. It is just a matter of putting right rule in the config file.

This patch sounds like an extension to user id problem where we want isolation between the processes of same user (process groups using different terminals). Would it make sense to generate some kind of kernel event for this and let user space execute the rules instead of creating this functionality in kernel.

This way once we extend this functionality to other subsystems, we can make it more flexible in user space. For example, create these groups for cpu controller but lets say not for block controller. Otherwise we will end up creating more kernel tunables for achieving same effect.

Thanks
Vivek

#65 lore raw
Quoted 2 lines · Vivek Goyal

Should this kind of thing be done in user space?

Almost certainly not.

First off, user-space is a fragmented mess. Just from a "let's get it done" angle, it just doesn't work. There are lots of different thing that create new tty's, and you can't have them all fixed. Plus it would be _way_ more code in user space than it is in kernel space.

Secondly, user-space daemons are a total mess. We've tried it many many times, and every time the _intention_ is to make things simpler to debug and deploy. And it almost never is. The interfaces end up being too painful, and the "part of the code is in kernel space, part of it is in user space" means that things just break all the time.

Finally, the whole "user space is more flexible" is just a lie. It simply doesn't end up being true. It will be _harder_ to configure some user-space daemon than it is to just set a flag in /sys or whatever. The "flexibility" tends to be more a flexibility to get things wrong than any actual advantage.

Just look at the patch in question. It's simple, it's clean, and it "just works". Doing the same thing in user space? It would be a total nightmare, and exactly _because_ it would be a total nightmare, the code would never be that simple or clean.

Linus

#66 lore raw
Quoted 11 lines · Linus Torvalds
Quoted 2 lines · Vivek Goyal

Should this kind of thing be done in user space?

Almost certainly not.

First off, user-space is a fragmented mess. Just from a "let's get it done" angle, it just doesn't work. There are lots of different thing that create new tty's, and you can't have them all fixed. Plus it would be _way_ more code in user space than it is in kernel space.

Quoted 6 lines

Secondly, user-space daemons are a total mess. We've tried it many many times, and every time the _intention_ is to make things simpler to debug and deploy. And it almost never is. The interfaces end up being too painful, and the "part of the code is in kernel space, part of it is in user space" means that things just break all the time.

Please elaborate, is this a generic statement or a comment on cgclassify or cgroup user rules.

Quoted 12 lines

Finally, the whole "user space is more flexible" is just a lie. It simply doesn't end up being true. It will be _harder_ to configure some user-space daemon than it is to just set a flag in /sys or whatever. The "flexibility" tends to be more a flexibility to get things wrong than any actual advantage.

Just look at the patch in question. It's simple, it's clean, and it "just works". Doing the same thing in user space? It would be a total nightmare, and exactly _because_ it would be a total nightmare, the code would never be that simple or clean.

Linus

Signature · 2 lines
	Three Cheers,
	Balbir
#67 lore raw
In reply to Vivek Goyal
Quoted 6 lines · Vivek Goyal

Mike,

IIUC, this automatically created task group is invisible to user space? I mean generally there is a task group associated with a cgroup and user space tools can walk through cgroup hierarchy to figure out how system is configured. Will that be possible with this patch.

No, it's dirt simple automation only at this point.

Quoted 4 lines

I am wondering what will happen to things like some kind of per cgroup stats. For example block controller keeps track of number of sectors transferred per cgroup. Hence this information will not be available for these internal task groups?

No, it won't. The target audience is those folks who don't _do_ the configuration they _could_ do, folks who don't use SCHED_IDLE or nice, or the power available through userspace cgroup tools.. folks who expect their box to "just work", out of the box.

Quoted 8 lines

Looks like everybody likes the idea but let me still ask the following question.

Should this kind of thing be done in user space? I mean what we are essentially doing providing isolation between two groups. That's why this cgroup infrastructure is in place. Just that currently how cgroups are created is fully depends on user space and kernel does not create cgroups of its own by default (ecept root cgroup).

I was of the same mind when Linus first broached the subject, but Ingo convinced me it was worth exploring because of the simple fact that people are not using the available tools.

Sadly, this includes distros.

AFAIK, no distro has cgroups configured and ready for aunt tilly, no distro has taught the GUI to use cgroups _at all_, even though it's trivial to launch self-reaping task groups from userspace.

Quoted 13 lines

I think systemd does something similar in the sense every system service it puts in a cgroup of its own on system startup.

libcgroup daemon has the facility to listen for kernel events (through netlink socket), and then put newly created tasks in cgroups as per the user spcefied rules in a config file. For example, if one wants isolation between tasks of two user ids, one can just write a rule and once the user logs in, its login session will be automatically placed in right cgroup. Hence one will be able to achieve isolation between two users. I think now it also has rules for classifying executables based on names/paths. So one can put "firefox" in one cgroup and say "make -j64" in a separate cgroup and provide isolation between two applications. It is just a matter of putting right rule in the config file.

I fiddled with configuring my system, but found options lacking. For instance, I found no way to automate per tty (or pgid in my case). I had to cobble scripts together to get the job done. Nothing that my distro delivered could just make it just happen for me.

When the tool mature, and distros use them, in kernel automation may well become more or less moot, but in the here and now, there is a target audience with a need that is not being serviced.

Quoted 5 lines

This patch sounds like an extension to user id problem where we want isolation between the processes of same user (process groups using different terminals). Would it make sense to generate some kind of kernel event for this and let user space execute the rules instead of creating this functionality in kernel.

Per user isn't very useful. The typical workstation has one user whacking away on the kbd/mouse. While you can identify firefox etc, it's not being done, and requires identifying every application. Heck, cgroups is built in, but userspace doesn't even mount. Nothing but nothing uses cgroups.

Quoted 4 lines

This way once we extend this functionality to other subsystems, we can make it more flexible in user space. For example, create these groups for cpu controller but lets say not for block controller. Otherwise we will end up creating more kernel tunables for achieving same effect.

I see your arguments, and agree to a large extent. As Linus noted, there are other advantages to in kernel automation, but for me, it all boils down to the fact that userspace is doing nothing with the tools.

ATM, cgroups is an enterprise or power user tool. The out of the box distro kernel user sees zero benefit for the overhead investment.

-Mike

#68 lore raw
Quoted 4 lines · Mike Galbraith

While you can identify firefox etc,
it's not being done, and requires identifying every application. Heck, cgroups is built in, but userspace doesn't even mount. Nothing but nothing uses cgroups.

The yet another init rewrite called systemd is supposedly cgroup happy.. No idea if its going to be useful though, I doubt its going to have an effect on me launching a konsole or the like, or screen creating a bunch of ttys.

#69 lore raw
Quoted 10 lines · Peter Zijlstra
Quoted 4 lines · Mike Galbraith

While you can identify firefox etc,
it's not being done, and requires identifying every application.  Heck, cgroups is built in, but userspace doesn't even mount.  Nothing but nothing uses cgroups.

The yet another init rewrite called systemd is supposedly cgroup happy.. No idea if its going to be useful though, I doubt its going to have an effect on me launching a konsole or the like, or screen creating a bunch of ttys.

systemd uses cgroups only for process tracking. No resource management. Though afaik, Lennart has some plans of doing resource management using systemd. I wonder how autogroups will interact with systemd in that case.

Dhaval

#70 lore raw
Quoted 13 lines · Dhaval Giani
Quoted 10 lines · Peter Zijlstra
Quoted 4 lines · Mike Galbraith

While you can identify firefox etc,
it's not being done, and requires identifying every application.  Heck, cgroups is built in, but userspace doesn't even mount.  Nothing but nothing uses cgroups.

The yet another init rewrite called systemd is supposedly cgroup happy.. No idea if its going to be useful though, I doubt its going to have an effect on me launching a konsole or the like, or screen creating a bunch of ttys.

Quoted 4 lines

systemd uses cgroups only for process tracking. No resource management. Though afaik, Lennart has some plans of doing resource management using systemd. I wonder how autogroups will interact with systemd in that case.

systemd already creates a named cgroup for each user who logs in and each session inside it. That's implemented via pam_systemd, which is enabled in all distros doing systemd. We create those groups right now only in the named "systemd" hierarchy, but iiuc then simply doing the same in the "cpu" hierarchy would have the exact same behaviour as this patch, but actually is based on a sane definition of what a session is.

Binding something like this to TTYs is just backwards. No graphical session has a TTY attached anymore. And there might be multiple TTYs used in the same session.

I really wonder why logic like this should live in kernel space at all, since a) the kernel has no real notion of a session, except audit and b) this is policy and as soon as people have this kind of group then they probably want other kind of autogrouping as well for the other controllers, which hence means userspace is a better, and configurable place for this.

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#71 lore raw
Quoted 2 lines · Lennart Poettering

Binding something like this to TTYs is just backwards.

Numbers talk, bullshit walks.

The numbers have been quoted. The clear interactive behavior has been seen.

And you're just full of bullshit.

Come back when you have something working and with numbers and better interactive performance. Until then, nobody cares.

Linus

#72 lore raw
Quoted 9 lines · Linus Torvalds
Quoted 2 lines · Lennart Poettering

Binding something like this to TTYs is just backwards.

Numbers talk, bullshit walks.

The numbers have been quoted. The clear interactive behavior has been seen.

Here's my super-complex patch btw, to achieve exactly the same thing from userspace without involving any kernel or systemd patching and kernel-side logic. Simply edit your own ~/.bashrc and add this to the end:

if [ "$PS1" ] ; then

          mkdir -m 0700 /sys/fs/cgroup/cpu/user/$$
          echo $$ > /sys/fs/cgroup/cpu/user/$$/tasks

fi

Then, as the superuser do this:

mount -t cgroup cgroup /sys/fs/cgroup/cpu -o cpu mkdir -m 0777 /sys/fs/cgroup/cpu/user

Done. Same effect. However: not crazy.

I am not sure I myself will find the time to prep some 'numbers' for you. They'd be the same as with the kernel patch anyway. But I am sure somebody else will do it for you...

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#73 lore raw
Quoted 23 lines · Lennart Poettering
Quoted 9 lines · Linus Torvalds
Quoted 2 lines · Lennart Poettering

Binding something like this to TTYs is just backwards.

Numbers talk, bullshit walks.

The numbers have been quoted. The clear interactive behavior has been seen.

Here's my super-complex patch btw, to achieve exactly the same thing from userspace without involving any kernel or systemd patching and kernel-side logic. Simply edit your own ~/.bashrc and add this to the end:

if [ "$PS1" ] ; then

          mkdir -m 0700 /sys/fs/cgroup/cpu/user/$$
          echo $$ > /sys/fs/cgroup/cpu/user/$$/tasks

fi

Then, as the superuser do this:

Quoted 8 lines

mount -t cgroup cgroup /sys/fs/cgroup/cpu -o cpu mkdir -m 0777 /sys/fs/cgroup/cpu/user

Done. Same effect. However: not crazy.

I am not sure I myself will find the time to prep some 'numbers' for you. They'd be the same as with the kernel patch anyway. But I am sure somebody else will do it for you...

Not quite the same, you're nesting one level deeper. But the reality is, not a lot of people will change their userspace.

#74 lore raw
Quoted 3 lines · Peter Zijlstra

Not quite the same, you're nesting one level deeper. But the reality is, not a lot of people will change their userspace.

That's a weak argument - not a lot of people will (explicitly) change their kernel either - they'll get a fresh kernel via their distro updates, as they would get userspace updates. So it's only a few people (distros) that actually need to make such a change.

Paul

#75 lore raw
Quoted 9 lines · Paul Menage
Quoted 3 lines · Peter Zijlstra

Not quite the same, you're nesting one level deeper. But the reality is, not a lot of people will change their userspace.

That's a weak argument - not a lot of people will (explicitly) change their kernel either - they'll get a fresh kernel via their distro updates, as they would get userspace updates. So it's only a few people (distros) that actually need to make such a change.

what is the downside of this patch going to be?

people who currently expect all the processes to compete equally will now find that they no longer do so. If I am understanding this correctly, this could mean that a box that was dedicated to running one application will now have that application no longer dominate the system, instead it will 'share equally' with the housekeeping apps on the system.

what would need to be done to revert to the prior situation?

David Lang

#76 lore raw
Quoted 14 lines · On Tue, 2010-11-16 at 10:55 -0800, [email protected] wrote:
Quoted 9 lines · Paul Menage
Quoted 3 lines · Peter Zijlstra

Not quite the same, you're nesting one level deeper. But the reality is, not a lot of people will change their userspace.

That's a weak argument - not a lot of people will (explicitly) change their kernel either - they'll get a fresh kernel via their distro updates, as they would get userspace updates. So it's only a few people (distros) that actually need to make such a change.

what is the downside of this patch going to be?

Quoted 7 lines

people who currently expect all the processes to compete equally will now find that they no longer do so. If I am understanding this correctly, this could mean that a box that was dedicated to running one application will now have that application no longer dominate the system, instead it will 'share equally' with the housekeeping apps on the system.

what would need to be done to revert to the prior situation?

build with: CONFIG_SCHED_AUTOGROUP=n,
boot with: noautogroup or
runtime: echo 0 > /proc/sysctl/kernel/sched_autogroup_enabled

(although the latter is a lazy one, it won't force existing tasks back to the root group)

#77 lore raw
Quoted 21 lines · Peter Zijlstra
Quoted 20 lines · On Tue, 2010-11-16 at 10:55 -0800, [email protected] wrote:
Quoted 9 lines · Paul Menage
Quoted 3 lines · Peter Zijlstra

Not quite the same, you're nesting one level deeper. But the reality is, not a lot of people will change their userspace.

That's a weak argument - not a lot of people will (explicitly) change their kernel either - they'll get a fresh kernel via their distro updates, as they would get userspace updates. So it's only a few people (distros) that actually need to make such a change.

what is the downside of this patch going to be?

people who currently expect all the processes to compete equally will now find that they no longer do so. If I am understanding this correctly, this could mean that a box that was dedicated to running one application will now have that application no longer dominate the system, instead it will 'share equally' with the housekeeping apps on the system.

Quoted 8 lines
Quoted 1 line

what would need to be done to revert to the prior situation?

build with: CONFIG_SCHED_AUTOGROUP=n,
boot with: noautogroup or
runtime: echo 0 > /proc/sysctl/kernel/sched_autogroup_enabled

(although the latter is a lazy one, it won't force existing tasks back to the root group)

I think this might create some issues with controllers which support some kind of upper limit on resource usage. These hidden group can practically consume any amount of resource and because use space tools can't see these, they will not be able to place a limit or control it.

If it is done from user space and cgroups are visible, then user can atleast monitor the resource usage and do something about it.

Thanks
Vivek

#78 lore raw
Quoted 7 lines · Vivek Goyal

I think this might create some issues with controllers which support some kind of upper limit on resource usage. These hidden group can practically consume any amount of resource and because use space tools can't see these, they will not be able to place a limit or control it.

If it is done from user space and cgroups are visible, then user can atleast monitor the resource usage and do something about it.

Its cpu-controller only, and then only for SCHED_OTHER tasks which are proportionally fair.

#79 lore raw
Quoted 7 lines · Peter Zijlstra
Quoted 5 lines · Vivek Goyal

I think this might create some issues with controllers which support some kind of upper limit on resource usage. These hidden group can practically consume any amount of resource and because use space tools can't see these, they will not be able to place a limit or control it.

Quoted 5 lines
Quoted 2 lines

If it is done from user space and cgroups are visible, then user can atleast monitor the resource usage and do something about it.

Its cpu-controller only, and then only for SCHED_OTHER tasks which are proportionally fair.

In this thread there are already mentions of extending it to block controller also which now supports the upper limit.

Secondly I am assuming at some point of time, cpu controller will also support throttling (including SCHED_OTHER class).

So as it is not a problem but the moment we start extending this logic to other controllers supporting upper limits it does pose the question that how do we handle it.

Even if we automatically create groups inside the kernel (because for all the reasons that why user space can't do it), it probably should create cgroups also so that these are visible to user space and not hidden.

Thanks
Vivek

#80 lore raw
Quoted 5 lines · Vivek Goyal

In this thread there are already mentions of extending it to block controller also which now supports the upper limit.

Secondly I am assuming at some point of time, cpu controller will also support throttling (including SCHED_OTHER class).

Possibly, but in both cases its voluntary -- that is, unless explicitly configured a cgroup can use as much as possible. These auto groups will use that.

If you want more control, create your own cgroup so you can poke at the parameters at will.

#81 lore raw
Quoted 7 lines · Peter Zijlstra
Quoted 5 lines · Vivek Goyal

In this thread there are already mentions of extending it to block controller also which now supports the upper limit.

Secondly I am assuming at some point of time, cpu controller will also support throttling (including SCHED_OTHER class).

Quoted 6 lines

Possibly, but in both cases its voluntary -- that is, unless explicitly configured a cgroup can use as much as possible. These auto groups will use that.

If you want more control, create your own cgroup so you can poke at the parameters at will.

I can create my own cgroups and control these. But one would like to control these hidden groups also. For example, in a clustered environment one might not want allow more than certain IOPS from machine X. In such scenario one would like to see all the groups and possibly put the limit.

Now one can argue that thse groups are under root and put overall limit on root and these groups get automatically controlled. But controllers like block and memory also support flat mode where all the groups are at same level.

		  pivot	
	        /   |   \
	       root  g2 g3

Here g2 and g3 are hidden auto groups and because it is flat configuration putting limit on root will not help and one can not limit g2 and g3 and one can not control total amount of IO from the system.

Now one can say use hierarchy and not flat setup. I am writting patches to enable hierarchy but that is disabled by default as flat setup came first and also there are concerns of accounting overhead etc.

So point being that these autogroups being hidden is a concern. Can we do something so that these groups show up in cgroup hierarchy and are then user controllable.

Thanks
Vivek

#82 lore raw
Quoted 4 lines · Vivek Goyal

So point being that these autogroups being hidden is a concern. Can we do something so that these groups show up in cgroup hierarchy and are then user controllable.

Well, either you disable the autogroup feature, or explicitly put each task in a taskgroup other than root and you'll be fine.

Nothing avoids that from working.

#83 lore raw
In reply to Vivek Goyal
Quoted 4 lines · Vivek Goyal

So point being that these autogroups being hidden is a concern. Can we do something so that these groups show up in cgroup hierarchy and are then user controllable.

I think that's valid, and I don't think it would be wrong to have some interface to show them.

Of course, then it's an interface question, and you'd want to be a bit careful in designing it.

Linus

#84 lore raw
In reply to Peter Zijlstra
Quoted 3 lines · Peter Zijlstra

Its cpu-controller only, and then only for SCHED_OTHER tasks which are proportionally fair.

Well, it's _currently_ CPU controller only. People have already wondered if we should try to do something similar for IO scheduling too.

So the thing I think is worth keeping in mind is that the "per-tty scheduling group" is really just an implementation issue. There is absolutely no question that it can't be about more than just scheduling, and that it can't be about more than just tty's also.

And an important thing to keep in mind is that "user interfaces are bad". The thinner the interface, the better. One of the reasons I really like autogroup is that it has _no_ interface at all. It's very much a heuristic, and it has zero user interface (apart from the knob that turns it on and off, of course). That is a great feature, because it means that you cannot break the interface. You will never need to have applications that have special linux-specific hooks in them, or system daemons who have to touch magical /proc files etc.

One of the problems I found annoying when just testing it using the plain cgroup interface (before the patch) was the resource management. You needed root, and they actually made sense requiring root, because I don't think we _want_ to allow people creating infinite numbers of cgroups. Vivek's "trivial patch" (shell script) is a major DoS thing, for example. Letting normal users create cgroups willy-nilly is not a good idea (and as Vivek already found out, his trivial script leaks cgroups in a pretty fundamental way).

The tty approach is somewhat self-limiting in that it requires you to get the tty to get an autogroup. But also, because it's very much a heuristic and doesn't have any user-visible interfaces, from a kernel perspective it's wonderful. There are no "semantics" to break. If it turns out that there is some way to create excessive cgroups, we can introduce per-user limits etc to say "the heuristic works up to X cgroups and then you'll just get your own user group". And nobody would ever notice.

So doing things automatically and without any user interface is about _more_ than just convenience. If it can be done that way, it is fundamentally better way to do things. Because it hides the implementation details, and leaves us open to do totally different things in the end.

For example, 'cgroups' itself is pretty heavy-weight, and is really quite smart. Those things nest, etc etc. But with the "it's just a heuristic", maybe somebody ends up doing a "simplified non-nesting grouping thing", and if you don't want the whole cgroup thing (I have always answered no to CONFIG_CGROUPS myself, for example), you could still do the autogrouping. But you could _not_ cleanly do the /proc/sys/cgroup/user scripting, because your implementation is no longer based on the whole cgroups thing.

Now, will any of this ever happen? I dunno. I doubt it will matter. But it's an example of why I think it's such a great approach, and why "it just works" is such an important feature.

Linus

Linus

#85 lore raw
In reply to Peter Zijlstra
Quoted 28 lines · Peter Zijlstra
Quoted 26 lines · Lennart Poettering
Quoted 9 lines · Linus Torvalds
Quoted 2 lines · Lennart Poettering

Binding something like this to TTYs is just backwards.

Numbers talk, bullshit walks.

The numbers have been quoted. The clear interactive behavior has been seen.

Here's my super-complex patch btw, to achieve exactly the same thing from userspace without involving any kernel or systemd patching and kernel-side logic. Simply edit your own ~/.bashrc and add this to the end:

if [ "$PS1" ] ; then

          mkdir -m 0700 /sys/fs/cgroup/cpu/user/$$
          echo $$ > /sys/fs/cgroup/cpu/user/$$/tasks

fi

Then, as the superuser do this:

mount -t cgroup cgroup /sys/fs/cgroup/cpu -o cpu mkdir -m 0777 /sys/fs/cgroup/cpu/user

Quoted 8 lines
Quoted 5 lines

Done. Same effect. However: not crazy.

I am not sure I myself will find the time to prep some 'numbers' for you. They'd be the same as with the kernel patch anyway. But I am sure somebody else will do it for you...

Not quite the same, you're nesting one level deeper. But the reality is, not a lot of people will change their userspace.

Well, remove the 'user' part of the path and you have the exact same behaviour.

Userspace usually gets updated way more frequently in most distributions than the kernel is. Maybe *you* never update userspace. But well, you are not the examplary Linux user, are you?

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#86 lore raw
Quoted 3 lines · Lennart Poettering

Userspace usually gets updated way more frequently in most distributions than the kernel is. Maybe *you* never update userspace. But well, you are not the examplary Linux user, are you?

I run very recent userspace on my desktop and laptop, and I get really tired of fixing it every upgrade. That said, I do run more recent kernels than get shipped, in fact I never run distro kernels at all.

#87 lore raw
In reply to Lennart Poettering
Quoted 4 lines · Lennart Poettering

Here's my super-complex patch btw, to achieve exactly the same thing from userspace without involving any kernel or systemd patching and kernel-side logic. Simply edit your own ~/.bashrc and add this to the end:

Right. And that's basically how this "patch" was actually tested originally - by doing this by hand, without actually having a patch in hand. I told people: this seems to work really well. Mike made it work automatically.

Because it's something we want to do it for all users, and for all shells, and make sure it gets done automatically. Including for users that have old distributions etc, and make it easy to do in one place. And then you do it for all the other heuristics we can see easily in the kernel. And then you do it magically without users even having to _notice_.

Suddenly it doesn't seem that wonderful any more to play with bashrc, does it?

That's the point. We can push out the kernel change, and everything will "just work". We can make that feature we already have in the kernel actually be _useful_.

User-level configuration for something that should just work is annoying. We can do better.

Put another way: if we find a better way to do something, we should _not_ say "well, if users want it, they can do this <technical thing here>". If it really is a better way to do something, we should just do it. Requiring user setup is _not_ a feature.

Now, I'm not saying that we shouldn't allow users to use cgroups. Of course they can do things manually too. But we shouldn't require users to do silly things that we can more easily do ourselves.

If the choice is between telling everybody "you should do this", and "we should just do this for you", I'll take the second one every time. We know it should be done. Why should we then tell somebody else to do it for us?

Linus

#88 lore raw
Quoted 2 lines · Linus Torvalds

User-level configuration for something that should just work is annoying. We can do better.

Completely agreed. Desktop users should not be required to fiddle with kernel knobs from userspace to fix interactivity problems. Having sane defaults applies to the kernel as much as it does to userspace.

Pekka

#89 lore raw
Quoted 8 lines · Pekka Enberg
Quoted 2 lines · Linus Torvalds

User-level configuration for something that should just work is annoying. We can do better.

Completely agreed. Desktop users should not be required to fiddle with kernel knobs from userspace to fix interactivity problems. Having sane defaults applies to the kernel as much as it does to userspace.

The only problem is that *desktop* users use *desktop* apps, which never have a controlling tty.

This is a *nerd* config not helping any regular desktop user. You guys are optimizing the system for people who build kernels and watch movies at the same time, not for desktop users. Hooking into TTYs works for almost nobody sane out there. :)

It's all fine, no comment on the patch, it's a nice hack. But please stop talking about the *desktop* here, it has almost nothing to do with it.

Kay

#90 lore raw
Quoted 3 lines · Kay Sievers

The only problem is that *desktop* users use *desktop* apps, which never have a controlling tty.

Yes. And have you noticed people complaining about stuttering while they run just their desktop app? No.

That's the thing. If you run a web browser and you use a flash player to view youtube or whatever in it, and only use other interactive desktop apps anyway, you won't see any problems _regardless_. It's not like the other desktop app you have open (and is idle, because you're not touching it) will matter.

Ask yourself who is complaining? _I_ have been complaining for years about desktop latency. I usually do it in private to developers, but trust me, I do it. Much of it has been about IO (the whole fsync fiasco), but some of it has been about the scheduler.

Look at who were trying out Con's patches. They were compiling things and running games at the same time. That's literally the kind of loads that people were looking at. Partly because that's the kinds of loads we haven't been good at. And it's the kind of load that this helps with.

Anyway, I find it depressing that now that this is solved, people come out of the woodwork and say "hey you could do this". Where were you guys a year ago or more?

Tough. I found out that I can solve it using cgroups, I asked people to comment and help, and I think the kernel approach is wonderful and _way_ simpler than the scripts I've seen. Yes, I'm biased ("kernels are easy - user space maintenance is a big pain").

Linus

#91 lore raw
In reply to Pekka Enberg
Quoted 9 lines · Pekka Enberg
Quoted 2 lines · Linus Torvalds

User-level configuration for something that should just work is annoying. We can do better.

Completely agreed. Desktop users should not be required to fiddle with kernel knobs from userspace to fix interactivity problems. Having sane defaults applies to the kernel as much as it does to userspace.

Jeez. Don't mentione the desktop. On the desktop this is compleltely irrelevant. There are not TTYs on the desktop. There's no "make -j" of the kernel tree on the desktop.

The kernel patch discussed here *has* *no* *relevance* for normal users.

The kernel patch discussed here is only relevant for people which start mplayer from one terminal, and "make -j" from another.

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#92 lore raw
Quoted 17 lines · Lennart Poettering
Quoted 10 lines · Pekka Enberg
Quoted 3 lines · Linus Torvalds

User-level configuration for something that should just work is annoying. We can do better.

Completely agreed. Desktop users should not be required to fiddle with kernel knobs from userspace to fix interactivity problems. Having sane defaults applies to the kernel as much as it does to userspace.

Jeez. Don't mentione the desktop. On the desktop this is compleltely irrelevant. There are not TTYs on the desktop. There's no "make -j" of the kernel tree on the desktop.

Quoted 8 lines

The kernel patch discussed here *has* *no* *relevance* for normal users.

The kernel patch discussed here is only relevant for people which start mplayer from one terminal, and "make -j" from another.

Lennart

Really, who uses Linux that is not a geek?

Signature · 5 lines
"They that give up essential liberty to obtain temporary safety,
deserve neither liberty nor safety."  (Ben Franklin)

"The course of history shows that as a government grows, liberty
decreases."  (Thomas Jefferson)
#93 lore raw
In reply to Linus Torvalds
Quoted 12 lines · Linus Torvalds
Quoted 4 lines · Lennart Poettering

Here's my super-complex patch btw, to achieve exactly the same thing from userspace without involving any kernel or systemd patching and kernel-side logic. Simply edit your own ~/.bashrc and add this to the end:

Right. And that's basically how this "patch" was actually tested originally - by doing this by hand, without actually having a patch in hand. I told people: this seems to work really well. Mike made it work automatically.

Quoted 8 lines

Because it's something we want to do it for all users, and for all shells, and make sure it gets done automatically. Including for users that have old distributions etc, and make it easy to do in one place. And then you do it for all the other heuristics we can see easily in the kernel. And then you do it magically without users even having to _notice_.

Suddenly it doesn't seem that wonderful any more to play with bashrc, does it?

agreed

Quoted 11 lines

That's the point. We can push out the kernel change, and everything will "just work". We can make that feature we already have in the kernel actually be _useful_.

User-level configuration for something that should just work is annoying. We can do better.

Put another way: if we find a better way to do something, we should _not_ say "well, if users want it, they can do this <technical thing here>". If it really is a better way to do something, we should just do it. Requiring user setup is _not_ a feature.

agreed, the question is if this really is a better way. It's definantly a change.

Quoted 8 lines

Now, I'm not saying that we shouldn't allow users to use cgroups. Of course they can do things manually too. But we shouldn't require users to do silly things that we can more easily do ourselves.

If the choice is between telling everybody "you should do this", and "we should just do this for you", I'll take the second one every time. We know it should be done. Why should we then tell somebody else to do it for us?

this is good for desktop interactivity because it no longer treats all processes equally, it give more CPU to processes that are running 'stand-alone' then it will to processes that are forked off from one master process.

In the desktop case where you really want something like 'make -j64' to be in the background and not interfere with the other tasks, but what about the situation of a dedicated web server? on that box the apache processes will get less CPU because they will all be in one group, and other things that happen on the box will get more CPU as they will be in different groups.

Is this really the right change to make?

having an option to do this sounds like a wonderful idea, and I would expect that desktop distros would want to have it default to 'on', but should it really be a default?

David Lang

#94 lore raw
Quoted 9 lines · On Tue, 16.11.10 11:08, [email protected] ([email protected]) wrote:
Quoted 4 lines

If the choice is between telling everybody "you should do this", and "we should just do this for you", I'll take the second one every time. We know it should be done. Why should we then tell somebody else to do it for us?

this is good for desktop interactivity because it no longer treats all processes equally, it give more CPU to processes that are running 'stand-alone' then it will to processes that are forked off from one master process.

This isn#t good for desktop interatctivey. It is *irrelevant* for desktop interactivity -- unless you define running "make -j" a typical desktop usecase. Which it isn't.

Just stop bringing about the word "desktop" here. It has no point in this discussion.

Quoted 1 line

In the desktop case where you really want something like 'make -j64'

No you don't. Because that is not a desktop use case.

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#95 lore raw
Quoted 2 lines · Lennart Poettering

No you don't. Because that is not a desktop use case.

See my other response. You don't care AT ALL, because by your judgement, all desktop is is a web browser and a word processor.

So why are you even discussing this? It's irrelevant for you. cgroups will _never_ matter for what you are talking about, and that has nothing to do with ttys, automation, scripting or anything else.

Because your definition of "desktop" seems to be "only interactive apps". So this i all irrelevant.

Which is fine by me. It's not what the patch is supposed to affect.

Linus

#96 lore raw
Quoted 8 lines · Linus Torvalds
Quoted 2 lines · Lennart Poettering

No you don't. Because that is not a desktop use case.

See my other response. You don't care AT ALL, because by your judgement, all desktop is is a web browser and a word processor.

Well, I do care. But I care more about *real* problems. For example the fact that "updatedb" makes your system sluggish while it runs. Or "man-db". Or anything else that runs from cron in the background.

Doing this tty dance won't help you much with background tasks such as man-db, updatedb and cron and its jobs, will it? They don't have ttys. Sorry for you. meh! Meh! meh! meh! meh!

(And along comes systemd, which actually handles this properly, since it actually has a proper notion of what a service is, and what a session is, and what an app is. And which hence can control all this sanely.)

Binding this to a tty is just solves a tiny bit of the real problem:
i.e. your own use of make -j. End of story.

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#97 lore raw
Quoted 19 lines · Lennart Poettering
Quoted 8 lines · Linus Torvalds
Quoted 2 lines · Lennart Poettering

No you don't. Because that is not a desktop use case.

See my other response. You don't care AT ALL, because by your judgement, all desktop is is a web browser and a word processor.

Well, I do care. But I care more about *real* problems. For example the fact that "updatedb" makes your system sluggish while it runs. Or "man-db". Or anything else that runs from cron in the background.

Doing this tty dance won't help you much with background tasks such as man-db, updatedb and cron and its jobs, will it? They don't have ttys. Sorry for you. meh! Meh! meh! meh! meh!

Quoted 10 lines

(And along comes systemd, which actually handles this properly, since it actually has a proper notion of what a service is, and what a session is, and what an app is. And which hence can control all this sanely.)

Binding this to a tty is just solves a tiny bit of the real problem:
i.e. your own use of make -j. End of story.

Lennart

Oh, you mean real problems like systemd can boot 10 seconds faster. What is 10 seconds when your system is up days or weeks?

Signature · 5 lines
"They that give up essential liberty to obtain temporary safety,
deserve neither liberty nor safety."  (Ben Franklin)

"The course of history shows that as a government grows, liberty
decreases."  (Thomas Jefferson)
#98 lore raw
In reply to Lennart Poettering
Quoted 1 line · On Tuesday 16 November 2010, 22:14:31 Lennart Poettering wrote:

On Tue, 16.11.10 12:38, Linus Torvalds ([email protected])

Quoted 16 lines · wrote:
Quoted 7 lines

On Tue, Nov 16, 2010 at 12:33 PM, Lennart Poettering

Quoted 1 line · <[email protected]> wrote:

No you don't. Because that is not a desktop use case.

See my other response. You don't care AT ALL, because by your judgement, all desktop is is a web browser and a word processor.

Well, I do care. But I care more about *real* problems. For example the fact that "updatedb" makes your system sluggish while it runs. Or "man-db". Or anything else that runs from cron in the background.

Doing this tty dance won't help you much with background tasks such as man-db, updatedb and cron and its jobs, will it? They don't have ttys. Sorry for you. meh! Meh! meh! meh! meh!

Quoted 7 lines

(And along comes systemd, which actually handles this properly, since it actually has a proper notion of what a service is, and what a session is, and what an app is. And which hence can control all this sanely.)

Binding this to a tty is just solves a tiny bit of the real problem:
i.e. your own use of make -j. End of story.

Lennart, would you mind pointing me the the paragraph that states, autogroup excludes any other improvements in this area?

In contrary, Linus clearly states, that this solves a long standing use case, that _he_ is suffering from a lot, and I bet, most of us in one or another way.. And it contains all that is needed: a fine selection of knobs for switching on/off that beast. Hopefully it can be taught to reveal some of its internal mechanics to the world, then all is fine.

If you think, that systemd can solve this and probably other aspects of responsiveness, go for it, compete with it, and _prove_ it with real facts and numbers, not just hand waving.

As already mentioned countless times (and some of it was even renamed for this very fact): the grouping by tty is just a starter. There are plenty of other possibilities to group the scheduling. The hard part is to find the right grouping concepts, that are making sense in the usability department _and_ are easy enough to be picked up from our favorite system and desktop environments. That's where the generic cgroup concept seems to be lacking ATM..

In one year from now on, our preferred distros will show, who won this competition. Probably both of you ;-)

Pete

#99 lore raw
Quoted 7 lines · Hans-Peter Jansen, le Thu 18 Nov 2010 23:33:46 +0100, a écrit :

As already mentioned countless times (and some of it was even renamed for this very fact): the grouping by tty is just a starter. There are plenty of other possibilities to group the scheduling. The hard part is to find the right grouping concepts, that are making sense in the usability department _and_ are easy enough to be picked up from our favorite system and desktop environments. That's where the generic cgroup concept seems to be lacking ATM..

Actually, cgroups should probably be completely hierarchical: sessions contain process groups, which contain processes, which contain threads. You could also gather sessions with the same uid.

Samuel

#100 lore raw
Quoted 9 lines · Samuel Thibault
Quoted 7 lines · Hans-Peter Jansen, le Thu 18 Nov 2010 23:33:46 +0100, a écrit :

As already mentioned countless times (and some of it was even renamed for this very fact): the grouping by tty is just a starter. There are plenty of other possibilities to group the scheduling. The hard part is to find the right grouping concepts, that are making sense in the usability department _and_ are easy enough to be picked up from our favorite system and desktop environments. That's where the generic cgroup concept seems to be lacking ATM..

Quoted 3 lines

Actually, cgroups should probably be completely hierarchical: sessions contain process groups, which contain processes, which contain threads. You could also gather sessions with the same uid.

Hierarchical is ~tempting, but adds overhead for not much gain.

-Mike

#101 lore raw
Quoted 10 lines · Mike Galbraith, le Thu 18 Nov 2010 16:35:51 -0700, a écrit :
Quoted 9 lines · Samuel Thibault
Quoted 7 lines · Hans-Peter Jansen, le Thu 18 Nov 2010 23:33:46 +0100, a écrit :

As already mentioned countless times (and some of it was even renamed for this very fact): the grouping by tty is just a starter. There are plenty of other possibilities to group the scheduling. The hard part is to find the right grouping concepts, that are making sense in the usability department _and_ are easy enough to be picked up from our favorite system and desktop environments. That's where the generic cgroup concept seems to be lacking ATM..

Quoted 5 lines
Quoted 3 lines

Actually, cgroups should probably be completely hierarchical: sessions contain process groups, which contain processes, which contain threads. You could also gather sessions with the same uid.

Hierarchical is ~tempting, but adds overhead for not much gain.

What overhead? The implementation of cgroups is actually already hierarchical.

Samuel

#102 lore raw
Quoted 3 lines · Samuel Thibault

What overhead? The implementation of cgroups is actually already hierarchical.

Well, at least the actual group creation overhead.

If it's a "only at setsid()", that's a fairly rare thing (although I think somebody might want to run something like the AIM7 benchmark - I have this memory of it doing lots of tty tests).

Or if it's only at "user launches new program from window manager", that's rare too.

But if you do it per process group, now you're doing one for each command invocation in a shell, for example. If you're doing things per thread, you've already lost.

Also, remember the goal: it was never about some theoretical end result. It's all about a simple heuristic that makes things work better. Trying to do that "perfectly" totally and utterly misses the whole point.

(google "Perfect is the enemy of good" - Voltaire)

Linus

#103 lore raw
Quoted 12 lines · Linus Torvalds, le Thu 18 Nov 2010 15:51:35 -0800, a écrit :
Quoted 3 lines · Samuel Thibault

What overhead? The implementation of cgroups is actually already hierarchical.

Well, at least the actual group creation overhead.

If it's a "only at setsid()", that's a fairly rare thing (although I think somebody might want to run something like the AIM7 benchmark - I have this memory of it doing lots of tty tests).

Quoted 5 lines

Or if it's only at "user launches new program from window manager", that's rare too.

But if you do it per process group, now you're doing one for each command invocation in a shell, for example.

Well, if it's from an interactive shell, it's not really a problem :)

But when it's from a script it can become one, yes. But are cgroups so expensive?

Quoted 1 line

If you're doing things per thread, you've already lost.

Not per thread, per process, i.e. put threads of the same process in the same cgroup. Again, I would have thought that creating a cgroup is very lightweight in front of a fork(). If not, maybe we are just looking for another, more lightweight container information that the scheduler would use [1], and keep more heavyweight containers for the non-automatic creation way.

Quoted 4 lines

Also, remember the goal: it was never about some theoretical end result. It's all about a simple heuristic that makes things work better. Trying to do that "perfectly" totally and utterly misses the whole point.

Sure. Using sid should already be quite good, but including the uid information as well should be easily even better.

Samuel

[1] Actually, I happen to have written a PhD thesis on this, that's why I'm popping up:)

#104 lore raw
Quoted 33 lines · Samuel Thibault, le Fri 19 Nov 2010 01:02:04 +0100, a écrit :
Quoted 12 lines · Linus Torvalds, le Thu 18 Nov 2010 15:51:35 -0800, a écrit :
Quoted 3 lines · Samuel Thibault

What overhead? The implementation of cgroups is actually already hierarchical.

Well, at least the actual group creation overhead.

If it's a "only at setsid()", that's a fairly rare thing (although I think somebody might want to run something like the AIM7 benchmark - I have this memory of it doing lots of tty tests).

Quoted 5 lines

Or if it's only at "user launches new program from window manager", that's rare too.

But if you do it per process group, now you're doing one for each command invocation in a shell, for example.

Well, if it's from an interactive shell, it's not really a problem :)

But when it's from a script it can become one, yes. But are cgroups so expensive?

Quoted 1 line

If you're doing things per thread, you've already lost.

Not per thread, per process, i.e. put threads of the same process in the same cgroup. Again, I would have thought that creating a cgroup is very lightweight in front of a fork(). If not, maybe we are just looking for another, more lightweight container information that the scheduler would use [1], and keep more heavyweight containers for the non-automatic creation way.

Quoted 7 lines
Quoted 4 lines

Also, remember the goal: it was never about some theoretical end result. It's all about a simple heuristic that makes things work better. Trying to do that "perfectly" totally and utterly misses the whole point.

Sure. Using sid should already be quite good, but including the uid information as well should be easily even better.

Also note that having a hierarchical process structure should permit to make things globally more efficient: avoid putting e.g. your cpp, cc1, and asm processes at three corners of your 4-socket NUMA machine :)

Samuel

#105 lore raw
Quoted 5 lines · Samuel Thibault

Also note that having a hierarchical process structure should permit to make things globally more efficient: avoid putting e.g. your cpp, cc1, and asm processes at three corners of your 4-socket NUMA machine :)

We have the hierarchy mandated by POSIX to track parents, childs, sessions and all that stuff, its just not the data structure used for scheduling.

And no, using that to load-balance between CPUs doesn't necessarily help with the NUMA case, load-balancing is an impossible job (equivalent to page-replacement -- you simply don't know the future), applications simply do wildly weird stuff.

Quoted 1 line

From a process hierarchy there's absolutely no difference between a

cc1/cpp/asm and some MPI jobs, both can be parent-child relations with pipes between, some just run short and have data affinity, others run long and don't have any.

#106 lore raw
Quoted 7 lines · Peter Zijlstra, le Fri 19 Nov 2010 12:57:24 +0100, a écrit :
Quoted 3 lines · Samuel Thibault

Also note that having a hierarchical process structure should permit to make things globally more efficient: avoid putting e.g. your cpp, cc1, and asm processes at three corners of your 4-socket NUMA machine :)

And no, using that to load-balance between CPUs doesn't necessarily help with the NUMA case,

It doesn't _necessarily_ help, but it should help in quite a few cases.

Quoted 3 lines

load-balancing is an impossible job (equivalent to page-replacement -- you simply don't know the future), applications simply do wildly weird stuff.

Sure. Not a reason not to get the low-hanging fruits :)

Quoted 4 lines

From a process hierarchy there's absolutely no difference between a cc1/cpp/asm and some MPI jobs, both can be parent-child relations with pipes between, some just run short and have data affinity, others run long and don't have any.

MPI jobs typically communicate with each other. Keeping them on the same socket permits to keep shared-memory MPI drivers to mostly remain in e.g. the L3 cache. That typically gives benefits.

Samuel

#107 lore raw
Quoted 10 lines · Samuel Thibault
Quoted 7 lines · Peter Zijlstra, le Fri 19 Nov 2010 12:57:24 +0100, a écrit :
Quoted 3 lines · Samuel Thibault

Also note that having a hierarchical process structure should permit to make things globally more efficient: avoid putting e.g. your cpp, cc1, and asm processes at three corners of your 4-socket NUMA machine :)

And no, using that to load-balance between CPUs doesn't necessarily help with the NUMA case,

It doesn't _necessarily_ help, but it should help in quite a few cases.

Colour me unconvinced, measuring shared cache footprint using PMUs might help (and people have actually implemented and played with that at various times in the past) but again, the added overhead of doing so will hurt a lot more workloads than might benefit.

Quoted 5 lines
Quoted 3 lines

load-balancing is an impossible job (equivalent to page-replacement -- you simply don't know the future), applications simply do wildly weird stuff.

Sure. Not a reason not to get the low-hanging fruits :)

I'm not at all convinced using the process hierarchy will really help much, but feel free to write the patch and test it. But making the migration condition very complex will definitely hurt some workloads.

Quoted 8 lines
Quoted 4 lines

From a process hierarchy there's absolutely no difference between a cc1/cpp/asm and some MPI jobs, both can be parent-child relations with pipes between, some just run short and have data affinity, others run long and don't have any.

MPI jobs typically communicate with each other. Keeping them on the same socket permits to keep shared-memory MPI drivers to mostly remain in e.g. the L3 cache. That typically gives benefits.

Pushing them away permits them to use a larger part of that same L3 cache allowing them to work on larger data sets. Most of the MPI apps have a large compute to communication ratio because that is what allows them to run in parallel so well (traditionally the interconnects were terribly slow to boot), that suggests that working on larger data sets is a good thing and running on the same node really doesn't matter since communication is assumes slow anyway.

There really is no simple solution to his.

#108 lore raw
Quoted 6 lines · Peter Zijlstra, le Fri 19 Nov 2010 15:43:13 +0100, a écrit :
Quoted 3 lines

MPI jobs typically communicate with each other. Keeping them on the same socket permits to keep shared-memory MPI drivers to mostly remain in e.g. the L3 cache. That typically gives benefits.

Pushing them away permits them to use a larger part of that same L3 cache allowing them to work on larger data sets.

But then you are not benefitting from all CPU cores.

Quoted 6 lines

Most of the MPI apps
have a large compute to communication ratio because that is what allows them to run in parallel so well (traditionally the interconnects were terribly slow to boot), that suggests that working on larger data sets is a good thing and running on the same node really doesn't matter since communication is assumes slow anyway.

Err, if the compute to communication ratio is big, then you should use all CPU cores, up to the point where communication becomes a matter again, and making sure that related MPI processes end up on the same socket will permit to got a it further.

Quoted 1 line

There really is no simple solution to his.

I never said there was even a solution, actually (in particular any kind of generic solution), but that there are a a few simple ways exist to make things better.

Samuel

#109 lore raw
In reply to Samuel Thibault
Quoted 6 lines · Samuel Thibault
Quoted 2 lines

If you're doing things per thread, you've already lost.

Not per thread, per process, i.e. put threads of the same process in the same cgroup. Again, I would have thought that creating a cgroup is very lightweight in front of a fork()

Absolutely not.

We have a good light-weight fork(). We try to avoid any extra allocations. We *definitely* don't want things at that level.

Seriously. I'd really like somebody running AIM7 just to see that even just doing it at setsid() doesn't hurt too badly. And that's something that happens once in a blue moon compared to fork and/or process groups.

Once per session is about as much as is acceptable. That's the kind of granularity we should look at. So things like "groups per user", "groups per session", "groups per one graphical application" are good. Not things that can happen tens of thousands of times a second.

Linus

#110 lore raw
Quoted 4 lines · Linus Torvalds, le Thu 18 Nov 2010 16:42:27 -0800, a écrit :

Once per session is about as much as is acceptable. That's the kind of granularity we should look at. So things like "groups per user", "groups per session", "groups per one graphical application" are good. Not things that can happen tens of thousands of times a second.

All right. I believe that should already work quite well both for desktop and servers indeed. "Per one graphical application" will most probably require desktop panel patching, however.

Samuel

#111 lore raw
Quoted 4 lines · Samuel Thibault

All right.  I believe that should already work quite well both for desktop and servers indeed.  "Per one graphical application" will most probably require desktop panel patching, however.

Sure. As mentioned somewhere earlier in this thread, I don't think this whole grouping thing is some "either or" black-or-white thing.

So I personally like the kernel patch because it's simple, and it "just works", regardless of what user space you happen to run. But that doesn't mean that user space couldn't easily give additional hints (to the point that if you end up having a distro that does hinting for everything, you could just turn off the kernel side entirely).

Linus

#112 lore raw
In reply to Samuel Thibault
Quoted 9 lines · Samuel Thibault
Quoted 4 lines · Linus Torvalds, le Thu 18 Nov 2010 16:42:27 -0800, a écrit :

Once per session is about as much as is acceptable. That's the kind of granularity we should look at. So things like "groups per user", "groups per session", "groups per one graphical application" are good. Not things that can happen tens of thousands of times a second.

All right. I believe that should already work quite well both for desktop and servers indeed. "Per one graphical application" will most probably require desktop panel patching, however.

I think that could be done with a fork flag with the same semantics as reset on fork. Once your task launcher (ala kdeinit) is tagged, it launches task groups, the children lose that ability. That requires userspace interaction though, dunno how you'd detect automatically.

-Mike

#113 lore raw
Quoted 3 lines · Mike Galbraith, le Thu 18 Nov 2010 18:12:37 -0700, a écrit :

I think that could be done with a fork flag with the same semantics as reset on fork. Once your task launcher (ala kdeinit) is tagged, it launches task groups, the children lose that ability.

Mmm, even if always creating groups can have a slight overhead, you should probably not prevent userspace from deciding to do so when it knows it's appropriate.

Samuel

#114 lore raw
Quoted 8 lines · Samuel Thibault
Quoted 3 lines · Mike Galbraith, le Thu 18 Nov 2010 18:12:37 -0700, a écrit :

I think that could be done with a fork flag with the same semantics as reset on fork. Once your task launcher (ala kdeinit) is tagged, it launches task groups, the children lose that ability.

Mmm, even if always creating groups can have a slight overhead, you should probably not prevent userspace from deciding to do so when it knows it's appropriate.

I think you may have misunderstood. The flag would be a hint that userland can set to say "I want to fork off task groups", just as SCHED_RESET_ON_FORK asks the kernel to reset a child's sched class and nice level on fork.

-Mike

#115 lore raw
Quoted 6 lines · Mike Galbraith, le Thu 18 Nov 2010 19:28:21 -0700, a écrit :
Quoted 5 lines · Samuel Thibault
Quoted 3 lines · Mike Galbraith, le Thu 18 Nov 2010 18:12:37 -0700, a écrit :

I think that could be done with a fork flag with the same semantics as reset on fork. Once your task launcher (ala kdeinit) is tagged, it launches task groups, the children lose that ability.

Quoted 8 lines
Quoted 3 lines

Mmm, even if always creating groups can have a slight overhead, you should probably not prevent userspace from deciding to do so when it knows it's appropriate.

I think you may have misunderstood. The flag would be a hint that userland can set to say "I want to fork off task groups", just as SCHED_RESET_ON_FORK asks the kernel to reset a child's sched class and nice level on fork.

Ah, ok. I would have rather done this per fork call, as there may be a difference between starting an application and starting a panel widget.

Samuel

#116 lore raw
In reply to Samuel Thibault
Quoted 2 lines · Samuel Thibault

What overhead? The implementation of cgroups is actually already hierarchical.

It must be nice to be that ignorant ;-) Speaking for the scheduler cgroup controller (that being the only one I actually know), most all the load-balance operations are O(n) in the number of active cgroups, and a lot of the cpu local schedule operations are O(d) where d is the depth of the cgroup tree.

[ and that's with the .38 targeted code, current mainline is O(n ln(n)) for load balancing and truly sucks on multi-socket ]

You add a lot of pointer chasing to all the scheduler fast paths and there is quite significant data size bloat for even compiling with the controller enabled, let alone actually using the stuff.

But sure, treat them as if they were free to use, I guess your machine is fast enough.

maintainer
#117 lore raw
Quoted 13 lines · Peter Zijlstra
Quoted 2 lines · Samuel Thibault

What overhead? The implementation of cgroups is actually already hierarchical.

It must be nice to be that ignorant ;-) Speaking for the scheduler cgroup controller (that being the only one I actually know), most all the load-balance operations are O(n) in the number of active cgroups, and a lot of the cpu local schedule operations are O(d) where d is the depth of the cgroup tree.

[ and that's with the .38 targeted code, current mainline is O(n ln(n)) for load balancing and truly sucks on multi-socket ]

Quoted 6 lines

You add a lot of pointer chasing to all the scheduler fast paths and there is quite significant data size bloat for even compiling with the controller enabled, let alone actually using the stuff.

But sure, treat them as if they were free to use, I guess your machine is fast enough.

In general though, I think you can say that: cgroups ass overhead. Simply because you add constraints, this means you need to 1) account more, 2) enforce constraints. Both have definite non-zero cost in both data and time.

#118 lore raw
Quoted 14 lines · * Peter Zijlstra ([email protected]) wrote:
Quoted 13 lines · Peter Zijlstra
Quoted 2 lines · Samuel Thibault

What overhead? The implementation of cgroups is actually already hierarchical.

It must be nice to be that ignorant ;-) Speaking for the scheduler cgroup controller (that being the only one I actually know), most all the load-balance operations are O(n) in the number of active cgroups, and a lot of the cpu local schedule operations are O(d) where d is the depth of the cgroup tree.

[ and that's with the .38 targeted code, current mainline is O(n ln(n)) for load balancing and truly sucks on multi-socket ]

Quoted 8 lines
Quoted 6 lines

You add a lot of pointer chasing to all the scheduler fast paths and there is quite significant data size bloat for even compiling with the controller enabled, let alone actually using the stuff.

But sure, treat them as if they were free to use, I guess your machine is fast enough.

In general though, I think you can say that: cgroups ass overhead.

I really think you meant "add" here ? (Hey! The keys were next to each other!) ;)

Quoted 3 lines

Simply because you add constraints, this means you need to 1) account more, 2) enforce constraints. Both have definite non-zero cost in both data and time.

Yep, this looks like one of these perpetual throughput vs latency trade-offs.

Thanks,

Mathieu

Signature · 4 lines
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
maintainer
#119 lore raw
Quoted 5 lines · Mathieu Desnoyers
Quoted 2 lines

In general though, I think you can say that: cgroups ass overhead.

I really think you meant "add" here ? (Hey! The keys were next to each other!) ;)

Uhm, quite!

Quoted 6 lines
Quoted 3 lines

Simply because you add constraints, this means you need to 1) account more, 2) enforce constraints. Both have definite non-zero cost in both data and time.

Yep, this looks like one of these perpetual throughput vs latency trade-offs.

Trade-off sure, throughput vs latency only in a specific use-case, its more a feature vs cost thing, just like all them trace people want lower cost tracing but want more features at the same time..

#120 lore raw
Quoted 1 line · * Peter Zijlstra ([email protected]) wrote:

On Fri, 2010-11-19 at 07:55 -0500, Mathieu Desnoyers wrote:

[...]

Quoted 5 lines
Quoted 2 lines

Yep, this looks like one of these perpetual throughput vs latency trade-offs.

Trade-off sure, throughput vs latency only in a specific use-case, its more a feature vs cost thing, just like all them trace people want lower cost tracing but want more features at the same time..

Yep, agreed for "feature vs cost", given that the kind of latency that is fixed in this case really boils down to a sluggish system under a relatively common workload -- so making this work might be called a "feature". ;)

FWIW, about tracing, well, we should distinguish between features that add cost to the fast-path and slow-path only.

But I really don't care anymore, since Ingo, Linus, Thomas and yourself made it very clear that you don't care. So let's not even start this discussion -- it's going nowhere.

Thanks,

Mathieu

Signature · 4 lines
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
#121 lore raw
In reply to Peter Zijlstra
Quoted 5 lines · Peter Zijlstra

It must be nice to be that ignorant ;-) Speaking for the scheduler cgroup controller (that being the only one I actually know), most all the load-balance operations are O(n) in the number of active cgroups, and a lot of the cpu local schedule operations are O(d) where d is the depth of the cgroup tree.

The same would apply to CPU autogroups, presumably?

Paul

#122 lore raw
Quoted 8 lines · Paul Menage
Quoted 5 lines · Peter Zijlstra

It must be nice to be that ignorant ;-) Speaking for the scheduler cgroup controller (that being the only one I actually know), most all the load-balance operations are O(n) in the number of active cgroups, and a lot of the cpu local schedule operations are O(d) where d is the depth of the cgroup tree.

The same would apply to CPU autogroups, presumably?

Yep, they're not special at all... uses the same mechanism.

#123 lore raw
Quoted 11 lines · Peter Zijlstra
Quoted 8 lines · Paul Menage
Quoted 5 lines · Peter Zijlstra

It must be nice to be that ignorant ;-) Speaking for the scheduler cgroup controller (that being the only one I actually know), most all the load-balance operations are O(n) in the number of active cgroups, and a lot of the cpu local schedule operations are O(d) where d is the depth of the cgroup tree.

The same would apply to CPU autogroups, presumably?

Yep, they're not special at all... uses the same mechanism.

The only difference is cost of creation and destruction, so cgroups and autogroups suck boulders of slightly different diameter when creating and/or destroying at high frequency.

-Mike

#124 lore raw
In reply to Peter Zijlstra
Quoted 3 lines · Peter Zijlstra

You add a lot of pointer chasing to all the scheduler fast paths and there is quite significant data size bloat for even compiling with the controller enabled, let alone actually using the stuff.

The pointer chasing hurts, but fast path + global spinlock is what slapped the hierarchical outta me :)

-Mike

#125 lore raw
In reply to Peter Zijlstra
Quoted 4 lines · Peter Zijlstra
Quoted 2 lines · Samuel Thibault

What overhead? The implementation of cgroups is actually already hierarchical.

Quoted 9 lines

It must be nice to be that ignorant ;-) Speaking for the scheduler cgroup controller (that being the only one I actually know), most all the load-balance operations are O(n) in the number of active cgroups, and a lot of the cpu local schedule operations are O(d) where d is the depth of the cgroup tree.

[ and that's with the .38 targeted code, current mainline is O(n ln(n)) for load balancing and truly sucks on multi-socket ]

I can say that for memory, with hierarchies we account all the way up, which can be a visible overhead, depending on how often you fault.

Signature · 2 lines
	Three Cheers,
	Balbir
#126 lore raw
In reply to Hans-Peter Jansen
Quoted 3 lines · Hans-Peter Jansen

If you think, that systemd can solve this and probably other aspects of responsiveness, go for it, compete with it, and _prove_ it with real facts and numbers, not just hand waving.

Of course systemd can do it. You don't even need systemd, autogroup or whatever else if you don't mind a little scripting. There's nothing to prove, any numbers he generates will be identical with any numbers I generate... it's the same scheduler whether you configure it from userspace or kernelspace.

Quoted 2 lines

As already mentioned countless times (and some of it was even renamed for this very fact): the grouping by tty is just a starter. There are

Actually, I switched to setsid alone for now at least, and the only thing in the root group is kernel threads.

-Mike

#127 lore raw
In reply to Lennart Poettering
Quoted 9 lines · On Tue, 16.11.10 11:08, [email protected] ([email protected]) wrote:
Quoted 9 lines
Quoted 4 lines

If the choice is between telling everybody "you should do this", and "we should just do this for you", I'll take the second one every time. We know it should be done. Why should we then tell somebody else to do it for us?

this is good for desktop interactivity because it no longer treats all processes equally, it give more CPU to processes that are running 'stand-alone' then it will to processes that are forked off from one master process.

Quoted 10 lines · Lennart Poettering

This isn#t good for desktop interatctivey. It is *irrelevant* for desktop interactivity -- unless you define running "make -j" a typical desktop usecase. Which it isn't.

Just stop bringing about the word "desktop" here. It has no point in this discussion.

Quoted 1 line

In the desktop case where you really want something like 'make -j64'

No you don't. Because that is not a desktop use case.

How about you stop bringing *your* narrow definition of "desktop" to the discussion? I am a kernel hacker but that doesn't mean I'm not also a desktop user. I shouldn't need to play with cgroups from userspace to keep music playing while I compile kernels. Things should just work.

Pekka

#128 lore raw
In reply to Linus Torvalds
Quoted 12 lines · Linus Torvalds
Quoted 4 lines · Lennart Poettering

Here's my super-complex patch btw, to achieve exactly the same thing from userspace without involving any kernel or systemd patching and kernel-side logic. Simply edit your own ~/.bashrc and add this to the end:

Right. And that's basically how this "patch" was actually tested originally - by doing this by hand, without actually having a patch in hand. I told people: this seems to work really well. Mike made it work automatically.

Quoted 7 lines

Because it's something we want to do it for all users, and for all shells, and make sure it gets done automatically. Including for users that have old distributions etc, and make it easy to do in one place. And then you do it for all the other heuristics we can see easily in the kernel. And then you do it magically without users even having to _notice_.

So what do you think about something like systemd handling it. systemd already does a lot of this stuff already in the form of process tracking, so it is quite trivial to do this. And more happily avoids all this complexity in the kernel.

Thanks,
Dhaval

#129 lore raw
Quoted 4 lines · On Martes, 16 de Noviembre de 2010 20:27:27 Dhaval Giani escribió:

So what do you think about something like systemd handling it. systemd already does a lot of this stuff already in the form of process tracking, so it is quite trivial to do this. And more happily avoids all this complexity in the kernel.

Note that even if this was a mistake, systemd can disable it and use its own heuristics. It would be nice to know what Lennart thinks about that, because it would be pointless to have a feature that eventually could be disabled in each boot in most distros.

#130 lore raw
In reply to Dhaval Giani
Quoted 5 lines · Dhaval Giani

So what do you think about something like systemd handling it. systemd already does a lot of this stuff already in the form of process tracking, so it is quite trivial to do this. And more happily avoids all this complexity in the kernel.

What complexity? Have you looked at the patch? It has no complexity anywhere.

It's a _lot_ less complex than having system daemons you don't control. We have not had good results with that approach in the past. System daemons tend to cause nasty problems, and debugging them is a nightmare.

Have you ever tried to debug the interaction between acpid and the kernel? I doubt it. It's not simple.

Did you ever try to understand how the old pcmcia code worked, with the user daemon to handle the add/remove requests?

Have you ever really worked with the "interesting" situations that come from the X server handling graphics mode switching, and thus being involved in suspend, resume and VT switching? Trust me, just designing the _interfaces_ to do that thing is nasty, and the code itself was a morass. There's a reason the graphics people wanted to put modeswitching in the kernel. Because doing it in a daemon is a f*cking pain in the ass.

Put another way: "do it in user space" usually does _not_ make things simpler.

For example: how do you do reference counting for a cgroup in user space, when processes fork and exit without you even knowing it? In kernel space, it's _trivial_. That's what kernel/autogroup.c does, and it has lots of support for it, because that kind of reference counting is exactly what the kernel does.

In a system daemon? Good luck with that. It's a nightmare. Maybe you could just poll all the cgroups, and try to remove them once a minute, and if they are empty it works. Or something like that. But what a hacky thing it would be.

And more importantly: I don't run systemd. Neither do a lot of other people. The way the patch does things, "it just works".

Did you go to the phoronix forum to look at how people reacted to the phoronix article about the patch? There were a number of testers. It was just so _easy_ to test and set up. If you want people to run some specific system daemon, it immediately gets much harder to set up and do.

Linus

#131 lore raw
Quoted 5 lines · Linus Torvalds

In a system daemon? Good luck with that. It's a nightmare. Maybe you could just poll all the cgroups, and try to remove them once a minute, and if they are empty it works. Or something like that. But what a hacky thing it would be.

There's an existing cgroups API - release_agent and notify_on_release
- whereby the kernel will spawn a userspace command once a given cgroup is completely empty. It's intended for pretty much exactly this purpose.

Paul

#132 lore raw
Quoted 8 lines · Paul Menage
Quoted 5 lines · Linus Torvalds

In a system daemon? Good luck with that. It's a nightmare. Maybe you could just poll all the cgroups, and try to remove them once a minute, and if they are empty it works. Or something like that. But what a hacky thing it would be.

Quoted 4 lines

There's an existing cgroups API - release_agent and notify_on_release
- whereby the kernel will spawn a userspace command once a given cgroup is completely empty. It's intended for pretty much exactly this purpose.

Yes. And it seems to be working just fine for me. I modiefied Lennart's script a bit to achieve that.

Addition to my .bashrc.

if [ "$PS1" ] ; then

        mkdir -m 0700 -p /cgroup/cpu/$$
        echo 1 > /cgroup/cpu/$$/notify_on_release
        echo $$ > /cgroup/cpu/$$/tasks

fi

I created one file /bin/rmcgroup to clean up the cgroup.

#!/bin/bash
rmdir /cgroup/cpu/$1

And did following to mount cgroup and setup empty group notification.

mount -t cgroup -o cpu none /cgroup/cpu
echo "/bin/rmcgroup" > /cgroup/cpu/release_agent

And it works fine. Upon ssh to my box, a cpu cgroup is automatically created and upon exiting the shell, this group is automatically destroyed.

So API/interface for automatically reclaiming the cgroup once it is empty seems to be pretty simple and works.

Thanks
Vivek

#133 lore raw
In reply to Linus Torvalds
Quoted 8 lines · Linus Torvalds
Quoted 5 lines · Dhaval Giani

So what do you think about something like systemd handling it. systemd already does a lot of this stuff already in the form of process tracking, so it is quite trivial to do this. And more happily avoids all this complexity in the kernel.

Quoted 6 lines

What complexity? Have you looked at the patch? It has no complexity anywhere.

It's a _lot_ less complex than having system daemons you don't control. We have not had good results with that approach in the past. System daemons tend to cause nasty problems, and debugging them is a nightmare.

Well, userspace doesn't bite.

Quoted 5 lines

For example: how do you do reference counting for a cgroup in user space, when processes fork and exit without you even knowing it? In kernel space, it's _trivial_. That's what kernel/autogroup.c does, and it has lots of support for it, because that kind of reference counting is exactly what the kernel does.

You can just do an rmdir from the cgroup release handler. Heck, "rmdir" is a pretty good GC in itself, since it deletes a cgroup only if it is empty.

Quoted 4 lines

In a system daemon? Good luck with that. It's a nightmare. Maybe you could just poll all the cgroups, and try to remove them once a minute, and if they are empty it works. Or something like that. But what a hacky thing it would be.

Well, that nightmare already exists. It's systemd. We use the cgroup release handler. If you ask me it's an aweful interface, but works fine.

Quoted 2 lines

And more importantly: I don't run systemd. Neither do a lot of other people. The way the patch does things, "it just works".

So this basically boils down to the fact that this is useful for your particular usecase. Because you don't want to update userspace. But don't claim this would be useful for anybody but you. It is definitely irrelevant for the usual desktop usecase.

Quoted 5 lines

Did you go to the phoronix forum to look at how people reacted to the phoronix article about the patch? There were a number of testers. It was just so _easy_ to test and set up. If you want people to run some specific system daemon, it immediately gets much harder to set up and do.

Jeez. Phoronix!

If you truly believe that the Phoronix usecase of running "make -j64" over the kernel tree was in any way relevant in real life for anybody but kernel developers, then I can't help you.

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#134 lore raw
Quoted 8 lines
Quoted 6 lines

What complexity? Have you looked at the patch? It has no complexity anywhere.

It's a _lot_ less complex than having system daemons you don't control. We have not had good results with that approach in the past. System daemons tend to cause nasty problems, and debugging them is a nightmare.

Well, userspace doesn't bite.

To me, this starts sounding like a legendary "userspace suspend" versus "kernel suspend" support fights many years ago... Since that, I have _sometimes_but_rarely_ been luckily enought to get my laptops recovered from the suspend states successfully...

And as the kernel patches allows disabling the schedyles feature either via kernel build parameters, boot parameters or at runtime _if_you_ really want, I do not understand why do you complaint if there will be much better defaults in the kernel immediately once you just build and boot the new kernel. (I really do not plan to start converting init-rd boot scripts from all of my computers systemd way of doing things in near future...)

Mika

#135 lore raw
Quoted 11 lines · Mika Laitio
Quoted 8 lines
Quoted 6 lines

What complexity? Have you looked at the patch? It has no complexity anywhere.

It's a _lot_ less complex than having system daemons you don't control. We have not had good results with that approach in the past. System daemons tend to cause nasty problems, and debugging them is a nightmare.

Well, userspace doesn't bite.

To me, this starts sounding like a legendary "userspace suspend" versus "kernel suspend" support fights many years ago...

Nah. Patch accepted, rejected, obsoleted 10 minutes from now etc etc, it'll never be a big deal.

-Mike

#136 lore raw
In reply to Linus Torvalds
Quoted 6 lines · Linus Torvalds

Because it's something we want to do it for all users, and for all shells, and make sure it gets done automatically. Including for users that have old distributions etc, and make it easy to do in one place. And then you do it for all the other heuristics we can see easily in the kernel. And then you do it magically without users even having to _notice_.

Well, this feature is pretty much interesting only for kernel hackers anyway. It is completely irrelevant for normal desktop people. Because
a) normal users don't use many terminals anyway and that very seldom and
b) if they do that they do not create gazillion of processes from one of the terminals and only very few in the other. Because only then this patch becomes relevant.

Heck, the patch wouldn't even have any effect on my machine (and I am hacker), because I tend to run my builds from within emacs. And since emacs has no TTY (since it is a X11/gtk build) it wouldn't be in its own scheduling group.

So, this patch only has an effect of people who build kernels from an xterm with make -j all day, and at the same time want to watch a movie, from a player they also start from a terminal, but from another one.

Only in such a setup this patch matters. And for everybody else it is completely irrelevant. If you don't use terminals it has no effect. If you don't run massivily parallel CPU hoggers it has no effect. If you do not run your mplayer from a terminal it has no effect.

The kernel bears your name, but that doesn't mean your own use of it was typical for more than you and a number kernel hackers like you.

Also, this implicit assumption that nobody would ever see this because people upgrade their kernels often and userspace seldom is just nonsense anyway. That's how *you* do it. But in reality userspace is updated for most folks way more often then the kernel is.

Or to turn this around: think how awesome that would be if we did this in userspace, then we could support older kernels too and wouldn't have to upgrade everything to your not-yet-released new kernel.

Suddenly it doesn't seem that wonderful anymore to play with the kernel to add this, does it?

Quoted 2 lines

Suddenly it doesn't seem that wonderful any more to play with bashrc, does it?

Well, I have no plans in pushing anybody to do this in bash really. All I am saying that tying this to a tty is crazy. And this is policy, and should be done in userspace, and we are almost there to do this in userspace.

In fact, I just prepped a patch to systemd to move every service and every user session into its own cgroup in the 'cpu' hierarchy (in addition to the group it already creates in the 'systemd' hierarchy). On a system that runs systemd for both managing users and sessions this means you are already half-way at what you want.

Quoted 2 lines

User-level configuration for something that should just work is annoying. We can do better.

Well, that says you, as a kernel hacker. For you it is easier to hack the kernel than to fiddle with the rest of the stack. Doesn't make it the right fix. You know, there are a lot of userspace folsk being afraid of and too lazy to hacking the kernel and hence rather workaround kernel fuckups in userspace then fixing it properly. But you are doing it the other way round, since userspace gives you the creeps you want to add something to the kernel that doesn't really have any value for anybody but a number of kernel folks, and is policy anyway, and what userspace people are working on anyway.

Quoted 4 lines

Put another way: if we find a better way to do something, we should _not_ say "well, if users want it, they can do this <technical thing here>". If it really is a better way to do something, we should just do it. Requiring user setup is _not_ a feature.

Well, if I make behaviour like this default in systemd, then this means there won't be user setup for this. Because the distros shipping systemd will get this as default behaviour.

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#137 lore raw

From: Lennart Poettering <[email protected]>
Date: Tue, 16 Nov 2010 21:28:39 +0100

Quoted 4 lines

Heck, the patch wouldn't even have any effect on my machine (and I am hacker), because I tend to run my builds from within emacs. And since emacs has no TTY (since it is a X11/gtk build) it wouldn't be in its own scheduling group.

Type 'tty' in that emacs shell, what does it give you?

It does get it's own TTY and it will thus get it's own scheduling group.

#138 lore raw
Quoted 10 lines · David Miller

From: Lennart Poettering <[email protected]>
Date: Tue, 16 Nov 2010 21:28:39 +0100

Quoted 4 lines

Heck, the patch wouldn't even have any effect on my machine (and I am hacker), because I tend to run my builds from within emacs. And since emacs has no TTY (since it is a X11/gtk build) it wouldn't be in its own scheduling group.

Type 'tty' in that emacs shell, what does it give you?

I am running "make -j", not a shell from emacs. it doesn't have a tty.

I just want to build something, not have terminal emulator in emacs.

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#139 lore raw

From: Lennart Poettering <[email protected]>
Date: Tue, 16 Nov 2010 22:08:04 +0100

Quoted 11 lines · David Miller
Quoted 10 lines

From: Lennart Poettering <[email protected]>
Date: Tue, 16 Nov 2010 21:28:39 +0100

Quoted 4 lines

Heck, the patch wouldn't even have any effect on my machine (and I am hacker), because I tend to run my builds from within emacs. And since emacs has no TTY (since it is a X11/gtk build) it wouldn't be in its own scheduling group.

Quoted 5 lines
Quoted 1 line

Type 'tty' in that emacs shell, what does it give you?

I am running "make -j", not a shell from emacs. it doesn't have a tty.

I just want to build something, not have terminal emulator in emacs.

It does the same exact thing. Please actually check and verify your facts instead of tossing back kneejerk responses.

davem@sunset:~/src/GIT$ cat Makefile
all:
	tty
davem@sunset:~/src/GIT$ 

"M-x compile" in that directory gives:

--------------------
-*- mode: compilation; default-directory: "~/src/GIT/" -*- Compilation started at Tue Nov 16 13:13:01

make -k
tty
/dev/pts/2

Compilation finished at Tue Nov 16 13:13:01
--------------------

#140 lore raw
In reply to Lennart Poettering
Quoted 3 lines

Well, if I make behaviour like this default in systemd, then this means there won't be user setup for this. Because the distros shipping systemd will get this as default behaviour.

And within the desktop where would you put this - in the window manager on the basis of top level windows or in the app startup ?

Alan

#141 lore raw
Quoted 6 lines · Alan Cox
Quoted 3 lines

Well, if I make behaviour like this default in systemd, then this means there won't be user setup for this. Because the distros shipping systemd will get this as default behaviour.

And within the desktop where would you put this - in the window manager on the basis of top level windows or in the app startup ?

Btw, I suspect either of these are reasonable. In fact, I don't think it would be at all wrong to have the desktop launcher have an option to "launch in a group" (although I think it would need to be named better than that). Right now, when you create desktop launchers under at least gnome, it allows you to specify a "type" for the application ("Application" or "Application in Terminal"), and maybe there could be a "CPU-bound application" choice that would set it in a CPU group of its own. Or whatever.

So I do _not_ believe that the autogroup feature should necessarily mean that you cannot do other grouping decisions too. I just do think that the whole notion of "it got started from a tty" is actually a very useful thing for legacy applications, and one where it's just simpler to do it in the kernel than build up any extra infrastructure for it.

So it's not necessarily at all an "either-or" thing.

Linus

#142 lore raw
Quoted 9 lines · Linus Torvalds
Quoted 6 lines · Alan Cox
Quoted 3 lines

Well, if I make behaviour like this default in systemd, then this means there won't be user setup for this. Because the distros shipping systemd will get this as default behaviour.

And within the desktop where would you put this - in the window manager on the basis of top level windows or in the app startup ?

Quoted 8 lines

Btw, I suspect either of these are reasonable. In fact, I don't think it would be at all wrong to have the desktop launcher have an option to "launch in a group" (although I think it would need to be named better than that). Right now, when you create desktop launchers under at least gnome, it allows you to specify a "type" for the application ("Application" or "Application in Terminal"), and maybe there could be a "CPU-bound application" choice that would set it in a CPU group of its own. Or whatever.

Well, my plan was actually to by default put everything into its own group, and then let users opt-out of that for specific processes, if the want to.

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#143 lore raw
Quoted 3 lines · Lennart Poettering

Well, my plan was actually to by default put everything into its own group, and then let users opt-out of that for specific processes, if the want to.

How many users are likely to do this, though?

I think you really want to make this be something which the application can specify by default that they should start in their own cgroup. One idea might be to it to the applications menu entry:

http://standards.freedesktop.org/desktop-entry-spec/latest/

... so there would be a new key value pair, "start_in_cgroup", that would allow the user to start an application in their own cgroup. It would be up to the desktop launcher to honor that if it was present.

One nice thing about having the desktop launch each application in its own cgroup is that it becomes easier for an desktop task manager to have a UI listing that lists things in a format which will be somewhat easier to understand than process listing. The cgroup would be a useful way to organize what is going on for each launched application, and it would allow people to see how much memory some application like evolution really requires. (On the other hand, maybe they would be happier not knowing. :-)

- Ted

#144 lore raw
Quoted 12 lines · Ted Ts'o
Quoted 3 lines · Lennart Poettering

Well, my plan was actually to by default put everything into its own group, and then let users opt-out of that for specific processes, if the want to.

How many users are likely to do this, though?

I think you really want to make this be something which the application can specify by default that they should start in their own cgroup. One idea might be to it to the applications menu entry:

Quoted 5 lines

http://standards.freedesktop.org/desktop-entry-spec/latest/

... so there would be a new key value pair, "start_in_cgroup", that would allow the user to start an application in their own cgroup. It would be up to the desktop launcher to honor that if it was present.

This is pretty much in line with what I want to do, except I want opt-out, not opt-in behaviour here.

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#145 lore raw
Quoted 4 lines · Lennart Poettering
Quoted 4 lines

I think you really want to make this be something which the application can specify by default that they should start in their own cgroup. One idea might be to it to the applications menu entry:

Quoted 8 lines
Quoted 5 lines

http://standards.freedesktop.org/desktop-entry-spec/latest/

... so there would be a new key value pair, "start_in_cgroup", that would allow the user to start an application in their own cgroup. It would be up to the desktop launcher to honor that if it was present.

This is pretty much in line with what I want to do, except I want opt-out, not opt-in behaviour here.

That works for me. One suggestion is that in addition to "opt-out", it should be also possible for an application launcher file to specify a specific cgroup name that should be used. That would allow multiple applications in a group to assigned to the same cgroup.

There also needs to be a way to start applications that are started via the GNOME and KDE session manager in a specified cgroup as well. I assume that's in your plan as well?

- Ted

#146 lore raw
Quoted 11 lines · Ted Ts'o
Quoted 10 lines · Lennart Poettering
Quoted 9 lines

I think you really want to make this be something which the application can specify by default that they should start in their own cgroup. One idea might be to it to the applications menu entry:

http://standards.freedesktop.org/desktop-entry-spec/latest/

... so there would be a new key value pair, "start_in_cgroup", that would allow the user to start an application in their own cgroup. It would be up to the desktop launcher to honor that if it was present.

Quoted 7 lines
Quoted 2 lines

This is pretty much in line with what I want to do, except I want opt-out, not opt-in behaviour here.

That works for me. One suggestion is that in addition to "opt-out", it should be also possible for an application launcher file to specify a specific cgroup name that should be used. That would allow multiple applications in a group to assigned to the same cgroup.

Being able to specify cgroup name/path is a good idea. That way one can make use of cgroup hierarchy also.

Thinking more about opt-in vs opt-out issue. Generally cgroups provide some kind of isolation between application groups and in the process can be somewhat expensive. More memory allocation, more accounting overhead and for CFQ block controller it can also mean additional idling and can result in overall reduced throughput.

Keeping that in mind, is it really a good idea to launch each application in a separate group. Will it be better to let user decide if the application should be launched in a separate cgroup?

The flip side is that how many people will really know about the functionality and will really launch application in a separate group. And may be it is a good idea to put everybody in a seprate cgroup by default even it means some cost so that if a application starts consuming too much of resources (make -j64), then its impact on rest of the groups can be contained.

I really don't have strong inclination for one over other. Just thinking loud...

Vivek

#147 lore raw
Quoted 13 lines · Vivek Goyal

Being able to specify cgroup name/path is a good idea. That way one can make use of cgroup hierarchy also.

Thinking more about opt-in vs opt-out issue. Generally cgroups provide some kind of isolation between application groups and in the process can be somewhat expensive. More memory allocation, more accounting overhead and for CFQ block controller it can also mean additional idling and can result in overall reduced throughput.

Keeping that in mind, is it really a good idea to launch each application in a separate group. Will it be better to let user decide if the application should be launched in a separate cgroup?

Quoted 8 lines

The flip side is that how many people will really know about the functionality and will really launch application in a separate group. And may be it is a good idea to put everybody in a seprate cgroup by default even it means some cost so that if a application starts consuming too much of resources (make -j64), then its impact on rest of the groups can be contained.

I really don't have strong inclination for one over other. Just thinking loud...

I wouldn't be too concerned here. It's not that we end up with 1000s of groups here. It's way < 40 or in the end, for a single user machine. Which I think isn't that bad.

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#148 lore raw
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

"Lennart" == Lennart Poettering <[email protected]> writes:

Lennart> I wouldn't be too concerned here. It's not that we end up Lennart> with 1000s of groups here. It's way < 40 or in the end, for a Lennart> single user machine. Which I think isn't that bad.

Ok, so how will this impact users on a single machine which is used to host VNC sessions? I've got machines with 30+ users all running one or more VNC sessions each on there. Nothing CPU bound generally, unless something freaks out and starts hogging a CPU. But will the overhead of 1000 groups be noticeable?

Thanks,
John

#149 lore raw
In reply to Ted Ts'o
Quoted 5 lines · Ted Ts'o

That works for me.  One suggestion is that in addition to "opt-out", it should be also possible for an application launcher file to specify a specific cgroup name that should be used.  That would allow multiple applications in a group to assigned to the same cgroup.

+1 for implementing this in systemd than in the kernel.

The userspace has much more info about which process needs to go into which group.

#150 lore raw
Quoted 8 lines · Andev
Quoted 5 lines · Ted Ts'o

That works for me. One suggestion is that in addition to "opt-out", it should be also possible for an application launcher file to specify a specific cgroup name that should be used. That would allow multiple applications in a group to assigned to the same cgroup.

Quoted 4 lines

+1 for implementing this in systemd than in the kernel.

The userspace has much more info about which process needs to go into which group.

-1 on your systemd/gnome/wm/etc. crap, that means it will become impossible to sanely switch off (gnome firmly believes knobs are evil), leaving everybody who _does_ know wth they're doing up a certain creek without a paddle.

#151 lore raw
Quoted 10 lines · Peter Zijlstra
Quoted 4 lines · Andev

+1 for implementing this in systemd than in the kernel.

The userspace has much more info about which process needs to go into which group.

-1 on your systemd/gnome/wm/etc. crap, that means it will become impossible to sanely switch off (gnome firmly believes knobs are evil), leaving everybody who _does_ know wth they're doing up a certain creek without a paddle.

Please, can we stop with this false dichotomy? This is decidedly not true and as Lennart has already pointed out, the knob already exists in systemd. You may like the kernel approach, but this does not mean there is no place for grouping driven by userspace.

- Ben

#152 lore raw
Quoted 9 lines · Ben Gamari
Quoted 4 lines

t means it will become
impossible to sanely switch off (gnome firmly believes knobs are evil), leaving everybody who _does_ know wth they're doing up a certain creek without a paddle.

Please, can we stop with this false dichotomy? This is decidedly not true and as Lennart has already pointed out, the knob already exists in systemd. You may like the kernel approach, but this does not mean there is no place for grouping driven by userspace.

Yes, and then at the next release, some idiotic GNOME engineer will decide to the "improve" the system by removing yet another knob....

-- Ted

#153 lore raw

From: Theodore Tso <[email protected]>
Date: Fri, 19 Nov 2010 08:07:12 -0500

Quoted 8 lines · Ben Gamari
Quoted 6 lines
Quoted 5 lines

t means it will become
impossible to sanely switch off (gnome firmly believes knobs are evil), leaving everybody who _does_ know wth they're doing up a certain creek without a paddle.

Quoted 7 lines
Quoted 4 lines

Please, can we stop with this false dichotomy? This is decidedly not true and as Lennart has already pointed out, the knob already exists in systemd. You may like the kernel approach, but this does not mean there is no place for grouping driven by userspace.

Yes, and then at the next release, some idiotic GNOME engineer will decide to the "improve" the system by removing yet another knob....

I have to agree on this one, there is no reason to believe that this trend will not continue and I've lost every ounce of optimism I've ever had in this area.

Besides, I'm having trouble believing someone who can't even get it through his thick skull that even "M-x compile" in emacs gives a TTY to the make process. Yet he kept claiming the opposite over and over again until I absolutely proved it to him, at which point he became completely silent on that line of reasoning.

Someone who argues and behaves in that way is not someone I put much stock in as far as implementing things in a wise or well informed way, it feels more like a mechanism which is being forced and rushed ahead without enough research or consideration.

#154 lore raw
Quoted 4 lines · David Miller

Someone who argues and behaves in that way is not someone I put much stock in as far as implementing things in a wise or well informed way, it feels more like a mechanism which is being forced and rushed ahead without enough research or consideration.

Wow, so much hate!

Love you too,

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#155 lore raw

From: Lennart Poettering <[email protected]>
Date: Fri, 19 Nov 2010 17:34:31 +0100

Quoted 8 lines · David Miller
Quoted 5 lines

Someone who argues and behaves in that way is not someone I put much stock in as far as implementing things in a wise or well informed way, it feels more like a mechanism which is being forced and rushed ahead without enough research or consideration.

Wow, so much hate!

Thanks for not addressing my technical arguments at all, which serves only to further my reservations.

#156 lore raw

Guys, calm down. It's really not -that- much of a deal.

We can do both. There is basically zero cost in the kernel: you can disabled the feature, and it's not like it's a maintenance headache (all the complexity that matters is the group scheduling itself, not the autogroup code that is well separated). And the kernel approach is useful to just take an otherwise unmodified system and just make it have nice default behavior.

And the user level approach? I think it's fine too. If you run systemd for other reasons (or if the gnome people add it to the task launcher or whatever), doing it there isn't wrong. I personally think it's somewhat disgusting to have a user-level callback with processes etc just to clean up a group, but whatever. As long as it's not common, who cares?

And you really _can_ combine them. As mentioned, I'd be nervous about AIM benchmarks. I keep mentioning AIM, but that's because it has shown odd tty issues before. Back when the RT guys wanted to do that crazy blocking BKL thing (replace the spinlock with a semaphore), AIM7 plummeted by 40%. And people were looking all over the place (file locking etc), and the tty layer was the biggest reason iirc.

Now, I don't know if AIM7 actually uses setsid() heavily etc, and it's possible it never hits it at all and only does read/write/kill. And it's not like AIM7 is the main thing we should look at regardless. But the point is that we know that there are some tty-heavy loads that are relevant, and it's very possible that a hybrid approach with "tty's handled automatically by the kernel, window manager does others in user space" would be a good way to avoid the (potential) nasty side of something that has a lot of short-lived tty connections.

So guys, calm down. We don't need to hate each other.

Linus

#157 lore raw
Quoted 7 lines · Linus Torvalds

And the user level approach? I think it's fine too. If you run systemd for other reasons (or if the gnome people add it to the task launcher or whatever), doing it there isn't wrong. I personally think it's somewhat disgusting to have a user-level callback with processes etc just to clean up a group, but whatever. As long as it's not common, who cares?

On that note, is there a good reason why the notify_on_release interface works the way it does? Wouldn't it be simpler if the cgroup simply provided a file on which a process (e.g. systemd) could block?

I guess it's a little too late at this point considering the old mechanism will still need to be supported, but it seems like this would provide a slightly cheaper cleanup path. Just my (perhaps flawed) two pence.

Quoted 3 lines

So guys, calm down. We don't need to hate each other.

Thanks for the nudge back to sanity.

Cheers,

- Ben

#158 lore raw
Quoted 4 lines · Ben Gamari

On that note, is there a good reason why the notify_on_release interface works the way it does? Wouldn't it be simpler if the cgroup simply provided a file on which a process (e.g. systemd) could block?

Actually, the sane interface would likely just be to have a "drop on release" interface.

Maybe some people really want to be _notified_. But my guess would that that just dropping the cgroup when it becomes empty would be at least a reasonable subset of users.

Who uses that thing now? The desktop launcher/systemd approach definitely doesn't seem want the overhead of being notified and having to remove it manually. Does anybody else really want it?

But that's really an independent question from all the other things. But with new cgroup users, it's very possible that it turns out that some of the original interfaces are just inconvenient and silly.

Linus

#159 lore raw
Quoted 9 lines · Linus Torvalds
Quoted 4 lines · Ben Gamari

On that note, is there a good reason why the notify_on_release interface works the way it does? Wouldn't it be simpler if the cgroup simply provided a file on which a process (e.g. systemd) could block?

Actually, the sane interface would likely just be to have a "drop on release" interface.

Hmm, I think automatic cleanup would be quite nice, but there are some niche cases to think about first (i.e. what do you do if a process generates a cgroup and wants to make itself a member of it, but then dies before it can do that, and the cgroup stays around but empty and never gets cleaned up).

Quoted 3 lines

Who uses that thing now? The desktop launcher/systemd approach definitely doesn't seem want the overhead of being notified and having to remove it manually. Does anybody else really want it?

I'd like automatic cleanup, but definitely also want to be notified when a cgroup runs empty. Here's the use case: we run apache in a cgroup (in a named hierarchy, not attached to any controller, we do this for keeping track of the service and all its children). Now apache dies. Its children, various CGI scripts stay around. However, since Apache is configured to be restarted systemd now kills all remaining children and waits until they are all gone, so that when it starts Apache anew we are in a clean and defined environment, and no remains of the previous instance remain. For this to work I need some kind of notification when all children are gone. Of course if systemd is PID 1, I can just use SIGCHLD for that, but that's more difficult when we are managing user porcesses, and want to do that with a PID != 1. And even even we are PID 1 its kinda neat to have an explicit notification for when a cgroup is empty instead of having to constantly check whether the cgroup is now empty after each SIGCHLD we receive.

Also, there must be a way to opt out of automatic cleanup for some groups, since it might make sense to give users access to subtrees of the hierarchy and if you clean up groups belonging to privileged code then you get a namespace problem because unprivileged code might recreate that group and confuse everybody.

So yeah, auto-cleanup is nice, but notifications I want too, please thanks.

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#160 lore raw
In reply to Ben Gamari
Quoted 3 lines · Ben Gamari

On that note, is there a good reason why the notify_on_release interface works the way it does? Wouldn't it be simpler if the cgroup simply provided a file on which a process (e.g. systemd) could block?

Backwards-compatibility with cpusets, which is what cgroups evolved from.

A delete_on_release option would be possible too, for the cases where there's really no entity that wants to do more than simply delete the group in question.

Paul

#161 lore raw
In reply to Ben Gamari
Quoted 9 lines · Ben Gamari
Quoted 7 lines · Linus Torvalds

And the user level approach? I think it's fine too. If you run systemd for other reasons (or if the gnome people add it to the task launcher or whatever), doing it there isn't wrong. I personally think it's somewhat disgusting to have a user-level callback with processes etc just to clean up a group, but whatever. As long as it's not common, who cares?

Quoted 3 lines

On that note, is there a good reason why the notify_on_release interface works the way it does? Wouldn't it be simpler if the cgroup simply provided a file on which a process (e.g. systemd) could block?

The notify_on_release interface is awful indeed. Feels like the old hotplug interface where each module request by the kernel caused a hotplug script to be spawned by the kernel.

However, I am not sure I like the idea of having pollable files like that, because in the systemd case I am very much interested in getting recursive notifications, i.e. I want to register once for getting notifications for a full subtree instead of having to register for each cgroup individually.

My personal favourite solution would be to get a netlink msg when a cgroup runs empty. That way multiple programs could listen to the events at the same time, and we'd have an easy way to subscribe to a whole hierarchy of groups.

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#162 lore raw
Quoted 25 lines · Lennart Poettering
Quoted 9 lines · Ben Gamari
Quoted 7 lines · Linus Torvalds

And the user level approach? I think it's fine too. If you run systemd for other reasons (or if the gnome people add it to the task launcher or whatever), doing it there isn't wrong. I personally think it's somewhat disgusting to have a user-level callback with processes etc just to clean up a group, but whatever. As long as it's not common, who cares?

Quoted 3 lines

On that note, is there a good reason why the notify_on_release interface works the way it does? Wouldn't it be simpler if the cgroup simply provided a file on which a process (e.g. systemd) could block?

The notify_on_release interface is awful indeed. Feels like the old hotplug interface where each module request by the kernel caused a hotplug script to be spawned by the kernel.

However, I am not sure I like the idea of having pollable files like that, because in the systemd case I am very much interested in getting recursive notifications, i.e. I want to register once for getting notifications for a full subtree instead of having to register for each cgroup individually.

Quoted 5 lines

My personal favourite solution would be to get a netlink msg when a cgroup runs empty. That way multiple programs could listen to the events at the same time, and we'd have an easy way to subscribe to a whole hierarchy of groups.

The netlink message should not be hard to do if we agree to work on it. The largest objections I've heard is that netlink implies network programming and most users want to be able to script in their automation and network scripting is hard.

Signature · 2 lines
	Three Cheers,
	Balbir
#163 lore raw
Quoted 11 lines · Balbir Singh
Quoted 10 lines

However, I am not sure I like the idea of having pollable files like that, because in the systemd case I am very much interested in getting recursive notifications, i.e. I want to register once for getting notifications for a full subtree instead of having to register for each cgroup individually.

My personal favourite solution would be to get a netlink msg when a cgroup runs empty. That way multiple programs could listen to the events at the same time, and we'd have an easy way to subscribe to a whole hierarchy of groups.

Quoted 4 lines

The netlink message should not be hard to do if we agree to work on it. The largest objections I've heard is that netlink implies network programming and most users want to be able to script in their automation and network scripting is hard.

Well, the notify_on_release stuff cannot be dropped anyway at this point in time, so netlink support would be an addition to, not a replacement for the current stuff that might be useful for scripting folks.

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#164 lore raw
Quoted 13 lines · Lennart Poettering
Quoted 11 lines · Balbir Singh
Quoted 10 lines

However, I am not sure I like the idea of having pollable files like that, because in the systemd case I am very much interested in getting recursive notifications, i.e. I want to register once for getting notifications for a full subtree instead of having to register for each cgroup individually.

My personal favourite solution would be to get a netlink msg when a cgroup runs empty. That way multiple programs could listen to the events at the same time, and we'd have an easy way to subscribe to a whole hierarchy of groups.

Quoted 8 lines
Quoted 4 lines

The netlink message should not be hard to do if we agree to work on it. The largest objections I've heard is that netlink implies network programming and most users want to be able to script in their automation and network scripting is hard.

Well, the notify_on_release stuff cannot be dropped anyway at this point in time, so netlink support would be an addition to, not a replacement for the current stuff that might be useful for scripting folks.

Agreed, we still need the old notify_on_release. Are you suggesting that for scripting we use the old interface and newer tools use netlink?

Signature · 2 lines
	Three Cheers,
	Balbir
#165 lore raw
Quoted 21 lines · Balbir Singh
Quoted 18 lines · Lennart Poettering
Quoted 15 lines · Balbir Singh
Quoted 10 lines

However, I am not sure I like the idea of having pollable files like that, because in the systemd case I am very much interested in getting recursive notifications, i.e. I want to register once for getting notifications for a full subtree instead of having to register for each cgroup individually.

My personal favourite solution would be to get a netlink msg when a cgroup runs empty. That way multiple programs could listen to the events at the same time, and we'd have an easy way to subscribe to a whole hierarchy of groups.

The netlink message should not be hard to do if we agree to work on it. The largest objections I've heard is that netlink implies network programming and most users want to be able to script in their automation and network scripting is hard.

Quoted 7 lines
Quoted 3 lines

Well, the notify_on_release stuff cannot be dropped anyway at this point in time, so netlink support would be an addition to, not a replacement for the current stuff that might be useful for scripting folks.

Agreed, we still need the old notify_on_release. Are you suggesting that for scripting we use the old interface and newer tools use netlink?

No, the contrary. I was referring to "the current stuff that might be useful for scripting and folks". And then netlink stuff would be for everything beyond scripting, but not scripting itself.

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#166 lore raw
In reply to Linus Torvalds
Quoted 6 lines · Linus Torvalds

And you really _can_ combine them. As mentioned, I'd be nervous about AIM benchmarks. I keep mentioning AIM, but that's because it has shown odd tty issues before. Back when the RT guys wanted to do that crazy blocking BKL thing (replace the spinlock with a semaphore), AIM7 plummeted by 40%. And people were looking all over the place (file locking etc), and the tty layer was the biggest reason iirc.

If I were home, I'd have already checked out AIM7 and more, but I didn't load laptop up with all my toys unfortunately. (or fortunately, since T130 ain't exactly a speed daemon)

-Mike

#167 lore raw
In reply to Ben Gamari
Quoted 7 lines · Ben Gamari
Quoted 6 lines · Peter Zijlstra
Quoted 4 lines · Andev

+1 for implementing this in systemd than in the kernel.

The userspace has much more info about which process needs to go into which group.

Quoted 8 lines
Quoted 4 lines

-1 on your systemd/gnome/wm/etc. crap, that means it will become impossible to sanely switch off (gnome firmly believes knobs are evil), leaving everybody who _does_ know wth they're doing up a certain creek without a paddle.

Please, can we stop with this false dichotomy? This is decidedly not true and as Lennart has already pointed out, the knob already exists in systemd.

It does for systemd, but he also said he's proposing patches for other userspace components (something gnome). And as we all know, gnome is not big on options, its their way or the high-way.

Quoted 2 lines

You may like the kernel approach, but this does not mean there
is no place for grouping driven by userspace.

My concern is that I don't want to go trawling through a dozen unrelated userspace package configurations to disable all this.

#168 lore raw
In reply to Linus Torvalds
Quoted 8 lines · Linus Torvalds

So I do _not_ believe that the autogroup feature should necessarily mean that you cannot do other grouping decisions too. I just do think that the whole notion of "it got started from a tty" is actually a very useful thing for legacy applications, and one where it's just simpler to do it in the kernel than build up any extra infrastructure for it.

So it's not necessarily at all an "either-or" thing.

As a last update to this messy discussion, here's a commit I made to systemd which ensures that every user session by default gets its own cgroup in the 'cpu' hierarchy.

http://cgit.freedesktop.org/systemd/commit/?id=74fe1fe36e35a26d764f1e3119d5f6d014db573c

And here's the one that does the same to ensure that every service systemd manages gets by default its own cgroup in the 'cpu' hierarchy.

http://cgit.freedesktop.org/systemd/commit/?id=d686d8a97bd7945af0a61504392d01a3167b576f

And finally, here's bugzilla entry for a patch I prepped for gnome-terminal's livte which creates a subgroup for each terminal widget shown by the same g-t instance:

https://bugzilla.gnome.org/show_bug.cgi?id=635119

With this all together we get quite a bit further than with the kernel patch, since we also cover all kinds of services, and treat user sessions equal to each other, and even users. (i.e. user A cannot get double the amount of CPU time simply by spawning double the amount of processes or double the amount of session thatn user B).

This should fix things for people with systemd and GNOME. Yes, all others are left in the cold. Sorry for that.

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#169 lore raw
Quoted 3 lines · Lennart Poettering

This should fix things for people with systemd and GNOME. Yes, all others are left in the cold. Sorry for that.

Is there an easy opt out for that, other than booting a CONFIG_CGROUP=n kernel?

#170 lore raw
Quoted 8 lines · Peter Zijlstra
Quoted 3 lines · Lennart Poettering

This should fix things for people with systemd and GNOME. Yes, all others are left in the cold. Sorry for that.

Is there an easy opt out for that, other than booting a CONFIG_CGROUP=n kernel?

systemd relies on CONFIG_CGROUP=y, since it useses it for service management. It creates its own name=systemd hierarchy for that with no controllers attached. If you turn that off, then systemd will refuse to boot. However, it does not rely on any of the controllers, and hence you are welcome to disable all cgroup controlls and systemd won't complain.

If you want to disable the automatic creation of groups in the 'cpu' hierarchy for user sessions then you can tell pam_systemd that by passing "controllers=" on the PAM config line. ("controllers=cpu" is the implied default.)

There's currently no global option to disable the same logic in systemd when it creates 'cpu' cgroups for the various services it runs. However, you can disable that individually with "ControlGroups=cpu:/" in the .service files. I will now add a global option as well.

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#171 lore raw
Quoted 8 lines · Lennart Poettering
Quoted 6 lines · Peter Zijlstra
Quoted 3 lines · Lennart Poettering

This should fix things for people with systemd and GNOME. Yes, all others are left in the cold. Sorry for that.

Quoted 7 lines
Quoted 2 lines

Is there an easy opt out for that, other than booting a CONFIG_CGROUP=n kernel?

systemd relies on CONFIG_CGROUP=y, since it useses it for service management. It creates its own name=systemd hierarchy for that with no controllers attached. If you turn that off, then systemd will refuse to boot.

Do expect distro bugzilla entries when this 'awesome'-ness hits the street.

Quoted 8 lines

However, it does not rely on any of the controllers, and hence you
are welcome to disable all cgroup controlls and systemd won't complain.

If you want to disable the automatic creation of groups in the 'cpu' hierarchy for user sessions then you can tell pam_systemd that by passing "controllers=" on the PAM config line. ("controllers=cpu" is the implied default.)

Quoted 4 lines

There's currently no global option to disable the same logic in systemd when it creates 'cpu' cgroups for the various services it runs. However, you can disable that individually with "ControlGroups=cpu:/" in the .service files. I will now add a global option as well.

A global knob is a must -- preferably with neon signs on so I can find it. Luckily I don't use this GNOME junk, otherwise I'd have had to ask how to revert that crap as well.

#172 lore raw
Quoted 30 lines · Peter Zijlstra
Quoted 8 lines · Lennart Poettering
Quoted 5 lines · Peter Zijlstra
Quoted 3 lines · Lennart Poettering

This should fix things for people with systemd and GNOME. Yes, all others are left in the cold. Sorry for that.

Quoted 8 lines
Quoted 3 lines

Is there an easy opt out for that, other than booting a CONFIG_CGROUP=n kernel?

systemd relies on CONFIG_CGROUP=y, since it useses it for service management. It creates its own name=systemd hierarchy for that with no controllers attached. If you turn that off, then systemd will refuse to boot.

Do expect distro bugzilla entries when this 'awesome'-ness hits the street.

Quoted 8 lines

However, it does not rely on any of the controllers, and hence you
are welcome to disable all cgroup controlls and systemd won't complain.

If you want to disable the automatic creation of groups in the 'cpu' hierarchy for user sessions then you can tell pam_systemd that by passing "controllers=" on the PAM config line. ("controllers=cpu" is the implied default.)

Quoted 11 lines
Quoted 5 lines

There's currently no global option to disable the same logic in systemd when it creates 'cpu' cgroups for the various services it runs. However, you can disable that individually with "ControlGroups=cpu:/" in the .service files. I will now add a global option as well.

A global knob is a must -- preferably with neon signs on so I can find it. Luckily I don't use this GNOME junk, otherwise I'd have had to ask how to revert that crap as well.

Amen!!

Signature · 5 lines
"They that give up essential liberty to obtain temporary safety,
deserve neither liberty nor safety."  (Ben Franklin)

"The course of history shows that as a government grows, liberty
decreases."  (Thomas Jefferson)
#173 lore raw
In reply to Lennart Poettering
Quoted 4 lines · Lennart Poettering

There's currently no global option to disable the same logic in systemd when it creates 'cpu' cgroups for the various services it runs. However, you can disable that individually with "ControlGroups=cpu:/" in the .service files. I will now add a global option as well.

This global option is now commited to systemd git.

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#174 lore raw
In reply to Alan Cox
Quoted 7 lines · Alan Cox
Quoted 4 lines

Well, if I make behaviour like this default in systemd, then this means there won't be user setup for this. Because the distros shipping systemd will get this as default behaviour.

And within the desktop where would you put this - in the window manager on the basis of top level windows or in the app startup ?

The plan with systemd is to make it manage both the system and the sessions. It's along the lines of what launchd does on MacOS: one instance for the system, another one for the user, because starting and supervising a system service and a session service are actually very very similar things.

In F15 we'll introduce systemd as an init system only. The next step will be to make it manage sessions too, and replace/augment gnome-session.

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#175 lore raw
In reply to Lennart Poettering
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

"LP" == Lennart Poettering <[email protected]> writes:

LP> Heck, the patch wouldn't even have any effect on my machine (and I am LP> hacker), because I tend to run my builds from within emacs. And since LP> emacs has no TTY (since it is a X11/gtk build) it wouldn't be in its own LP> scheduling group.

M-x compile uses a pty to talk to its sub-process, so if ptys get their own cgroup then a compile w/in emacs will, too.

(Try using tty as the command M-x compile should run to confirm.)

-JimC

Signature · 1 line
James Cloos <[email protected]>         OpenPGP: 1024D/ED7DAEA6
#176 lore raw
In reply to Linus Torvalds
Quoted 4 lines · Linus Torvalds

That's the point. We can push out the kernel change, and everything will "just work". We can make that feature we already have in the kernel actually be _useful_.

This is absolutely fine, as long as everyone wants the feature, "just works" for us as kernel developers might not be the same as "just works" for all end users. How does one find and disable this feature if one is not happy? I don't think it is very hard, it could be a simple tool that the distro provides or documentation, but hidden works both ways, IMHO. In summary, we need tooling with good defaults.

Signature · 2 lines
	Three Cheers,
	Balbir
#177 lore raw
In reply to Lennart Poettering
Quoted 26 lines · Lennart Poettering
Quoted 9 lines · Linus Torvalds
Quoted 2 lines · Lennart Poettering

Binding something like this to TTYs is just backwards.

Numbers talk, bullshit walks.

The numbers have been quoted. The clear interactive behavior has been seen.

Here's my super-complex patch btw, to achieve exactly the same thing from userspace without involving any kernel or systemd patching and kernel-side logic. Simply edit your own ~/.bashrc and add this to the end:

   if [ "$PS1" ] ; then
           mkdir -m 0700 /sys/fs/cgroup/cpu/user/$$
           echo $$>  /sys/fs/cgroup/cpu/user/$$/tasks
   fi

Then, as the superuser do this:

mount -t cgroup cgroup /sys/fs/cgroup/cpu -o cpu mkdir -m 0777 /sys/fs/cgroup/cpu/user

Quoted 9 lines

Done. Same effect. However: not crazy.

I am not sure I myself will find the time to prep some 'numbers' for you. They'd be the same as with the kernel patch anyway. But I am sure somebody else will do it for you...

Lennart

So you have tested this and have a nice demo and numbers to back it up?

Signature · 5 lines
"They that give up essential liberty to obtain temporary safety,
deserve neither liberty nor safety."  (Ben Franklin)

"The course of history shows that as a government grows, liberty
decreases."  (Thomas Jefferson)
#178 lore raw
Quoted 25 lines · Stephen Clark
Quoted 24 lines · Lennart Poettering
Quoted 6 lines · Linus Torvalds
Quoted 1 line · Lennart Poettering

Binding something like this to TTYs is just backwards.

Numbers talk, bullshit walks.

The numbers have been quoted. The clear interactive behavior has been seen.

Here's my super-complex patch btw, to achieve exactly the same thing from userspace without involving any kernel or systemd patching and kernel-side logic. Simply edit your own ~/.bashrc and add this to the end:

if [ "$PS1" ] ; then

          mkdir -m 0700 /sys/fs/cgroup/cpu/user/$$
          echo $$>  /sys/fs/cgroup/cpu/user/$$/tasks

fi

Then, as the superuser do this:

mount -t cgroup cgroup /sys/fs/cgroup/cpu -o cpu mkdir -m 0777 /sys/fs/cgroup/cpu/user

Done. Same effect. However: not crazy.

Quoted 7 lines
Quoted 6 lines

I am not sure I myself will find the time to prep some 'numbers' for you. They'd be the same as with the kernel patch anyway. But I am sure somebody else will do it for you...

Lennart

So you have tested this and have a nice demo and numbers to back it up?

I just modified my .bashrc and for my each ssh session, it seems to be working fine and creating a cgroup as soon as I ssh into the box.

Just that it will need little modification to reap the group automatically when shell exits.

Thanks
Vivek

#179 lore raw
Quoted 5 lines · Vivek Goyal

I just modified my .bashrc and for my each ssh session, it seems to be working fine and creating a cgroup as soon as I ssh into the box.

Just that it will need little modification to reap the group automatically when shell exits.

Yeah, self-reaping launcher scripts work fine, I cobbled one together. There's no question that the infrastructure works.

-Mike

#180 lore raw
In reply to Stephen Clark
Quoted 5 lines · Stephen Clark
Quoted 3 lines

I am not sure I myself will find the time to prep some 'numbers' for you. They'd be the same as with the kernel patch anyway. But I am sure somebody else will do it for you...

So you have tested this and have a nice demo and numbers to back it up?

Yes, I tested this. And I thought the paragraph was explicit enough about the numbers.

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#181 lore raw
In reply to Lennart Poettering
Quoted 23 lines · Lennart Poettering
Quoted 9 lines · Linus Torvalds
Quoted 2 lines · Lennart Poettering

Binding something like this to TTYs is just backwards.

Numbers talk, bullshit walks.

The numbers have been quoted. The clear interactive behavior has been seen.

Here's my super-complex patch btw, to achieve exactly the same thing from userspace without involving any kernel or systemd patching and kernel-side logic. Simply edit your own ~/.bashrc and add this to the end:

if [ "$PS1" ] ; then

          mkdir -m 0700 /sys/fs/cgroup/cpu/user/$$
          echo $$ > /sys/fs/cgroup/cpu/user/$$/tasks

fi

Then, as the superuser do this:

Quoted 8 lines

mount -t cgroup cgroup /sys/fs/cgroup/cpu -o cpu mkdir -m 0777 /sys/fs/cgroup/cpu/user

Done. Same effect. However: not crazy.

I am not sure I myself will find the time to prep some 'numbers' for you. They'd be the same as with the kernel patch anyway. But I am sure somebody else will do it for you...

OK, I've done some tests and the result is that Lennart's approach seems to work best. It also _feels_ better interactively compared to the vanilla kernel and in-kernel cgrougs on my machine. Also it's really nice to have an interface to actually see what is going on. With the kernel patch you're totally in the dark about what is going on right now.

Here are some numbers all recorded while running a make -j4 job in one shell.

perf sched record sleep 30
perf trace -s /usr/libexec/perf-core/scripts/perl/wakeup-latency.pl :

vanilla kernel without cgroups:
total_wakeups: 44306
avg_wakeup_latency (ns): 36784
min_wakeup_latency (ns): 0
max_wakeup_latency (ns): 9378852

with in-kernel patch:
total_wakeups: 43836
avg_wakeup_latency (ns): 67607
min_wakeup_latency (ns): 0
max_wakeup_latency (ns): 8983036

with Lennart's approach:
total_wakeups: 51070
avg_wakeup_latency (ns): 29047
min_wakeup_latency (ns): 0
max_wakeup_latency (ns): 10008237

perf record -a -e sched:sched_switch -e sched:sched_wakeup sleep 10 perf trace -s /usr/libexec/perf-core/scripts/perl/wakeup-latency.pl :

without cgroups:
total_wakeups: 13195
avg_wakeup_latency (ns): 48484
min_wakeup_latency (ns): 0
max_wakeup_latency (ns): 8722497

with in-kernel approach:
total_wakeups: 14106
avg_wakeup_latency (ns): 92532
min_wakeup_latency (ns): 20
max_wakeup_latency (ns): 5642393

Lennart's approach:
total_wakeups: 22215
avg_wakeup_latency (ns): 24118
min_wakeup_latency (ns): 0
max_wakeup_latency (ns): 8001142

Signature · 1 line
Markus
#182 lore raw
In reply to Lennart Poettering
Quoted 3 lines · Lennart Poettering

Binding something like this to TTYs is just backwards. No graphical session has a TTY attached anymore. And there might be multiple TTYs used in the same session.

Using a group per tty makes sense for us console jockeys..

Anyway, nobody uses systemd yet and afaik not all distro's even plan on using it (I know I'm not waiting to learn yet another init variant).

#183 lore raw
Quoted 11 lines · Peter Zijlstra
Quoted 4 lines · Lennart Poettering

Binding something like this to TTYs is just backwards. No graphical session has a TTY attached anymore. And there might be multiple TTYs used in the same session.

Using a group per tty makes sense for us console jockeys..

Anyway, nobody uses systemd yet and afaik not all distro's even plan on using it (I know I'm not waiting to learn yet another init variant).

Right on!

Quoted 7 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Signature · 5 lines
"They that give up essential liberty to obtain temporary safety,
deserve neither liberty nor safety."  (Ben Franklin)

"The course of history shows that as a government grows, liberty
decreases."  (Thomas Jefferson)
#184 lore raw
In reply to Peter Zijlstra
Quoted 7 lines · Peter Zijlstra
Quoted 3 lines · Lennart Poettering

Binding something like this to TTYs is just backwards. No graphical session has a TTY attached anymore. And there might be multiple TTYs used in the same session.

Using a group per tty makes sense for us console jockeys..

Well, then maybe you shouldn't claim this was relevant for anybody but yourself. Because it is irrelevant for most users if it is bound to the TTY.

Quoted 2 lines

Anyway, nobody uses systemd yet and afaik not all distro's even plan on using it (I know I'm not waiting to learn yet another init variant).

Well, the way it looks right now we have convinced all big distros.

Lennart

Signature · 1 line
Lennart Poettering - Red Hat, Inc.
#185 lore raw
Quoted 4 lines · Lennart Poettering
Quoted 1 line

Using a group per tty makes sense for us console jockeys..

Well, then maybe you shouldn't claim this was relevant for anybody but yourself. Because it is irrelevant for most users if it is bound to the TTY.

I never made such a claim, in fact my kernels will have this =n.

Quoted 4 lines
Quoted 2 lines

Anyway, nobody uses systemd yet and afaik not all distro's even plan on using it (I know I'm not waiting to learn yet another init variant).

Well, the way it looks right now we have convinced all big distros.

Crap, means I get to blame you for more than non working sound. I get really fed up having to re-discover how to fix broken init scripts on every upgrade :/

#186 lore raw
In reply to Lennart Poettering
Quoted 4 lines · Lennart Poettering
Quoted 2 lines · Peter Zijlstra

On Tue, 2010-11-16 at 18:03 +0100, Lennart Poettering wrote:

Quoted 9 lines
Quoted 5 lines
Quoted 3 lines

Binding something like this to TTYs is just backwards. No graphical session has a TTY attached anymore. And there might be multiple TTYs used in the same session.

Using a group per tty makes sense for us console jockeys..

Well, then maybe you shouldn't claim this was relevant for anybody but yourself. Because it is irrelevant for most users if it is bound to the TTY.

For what it's worth, I suspect that the object that should be bound to is probably not the tty, but rather the session ID of the process (which generally is 1:1 with controlling TTY for console processes.)

-hpa

#187 lore raw
Quoted 11 lines · H. Peter Anvin, le Thu 18 Nov 2010 16:35:59 -0800, a écrit :
Quoted 10 lines · Lennart Poettering
Quoted 7 lines · Peter Zijlstra
Quoted 3 lines · Lennart Poettering

Binding something like this to TTYs is just backwards. No graphical session has a TTY attached anymore. And there might be multiple TTYs used in the same session.

Using a group per tty makes sense for us console jockeys..

Quoted 7 lines
Quoted 3 lines

Well, then maybe you shouldn't claim this was relevant for anybody but yourself. Because it is irrelevant for most users if it is bound to the TTY.

For what it's worth, I suspect that the object that should be bound to is probably not the tty, but rather the session ID of the process (which generally is 1:1 with controlling TTY for console processes.)

Agreed.

That'll catch both the tty case (implemented by the proposed patch), and the rest.

Samuel

#188 lore raw
Quoted 16 lines · * Samuel Thibault ([email protected]) wrote:
Quoted 15 lines · H. Peter Anvin, le Thu 18 Nov 2010 16:35:59 -0800, a écrit :
Quoted 13 lines · Lennart Poettering
Quoted 7 lines · Peter Zijlstra
Quoted 3 lines · Lennart Poettering

Binding something like this to TTYs is just backwards. No graphical session has a TTY attached anymore. And there might be multiple TTYs used in the same session.

Using a group per tty makes sense for us console jockeys..

Well, then maybe you shouldn't claim this was relevant for anybody but yourself. Because it is irrelevant for most users if it is bound to the TTY.

Quoted 8 lines
Quoted 3 lines

For what it's worth, I suspect that the object that should be bound to is probably not the tty, but rather the session ID of the process (which generally is 1:1 with controlling TTY for console processes.)

Agreed.

That'll catch both the tty case (implemented by the proposed patch), and the rest.

This really does make a lot of sense. Tying on the Session ID rather than the TTY would allow to deal with graphical applications by letting them specify session IDs with setsid() when the application starts. It seems much more generic than TTY, and maps to TTY already.

Thanks,

Mathieu

Signature · 4 lines
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com

A message that is not in the archive (sent off-list or not yet received): [email protected]

#190 lore raw
Quoted 5 lines · Balbir Singh

I see the arguments you make, but it sounds easily fixable, we need to provide a simple template and a guide. I suspect the question that eventually comes to mind is - what is the overhead of maintaining or guaranteeing that behaviour and will the user still need to configure things in user space to get the full benefit?

That systemd gizmo sounds like it'll eventually take care of new installations, though a tad costlier (for which you likely get more tweakability).

-Mike

#191 lore raw
In reply to Mike Galbraith
Quoted 5 lines · Mike Galbraith
Quoted 2 lines · Oleg Nesterov

And the exiting task can do a lot before it disappears, probably we shouldn't ignore ->autogroup.

I don't really understand what makes the exiting task different, but OK.

However, I must admit I dislike this check. Because, looking at this code, it is not clear why do we check PF_EXITING. It looks as if it is needed for correctness.

OK, this is minor. I think the patch is correct, just one nit below.

Quoted 6 lines
Quoted 2 lines

It can't protect the change of signal->autogroup, multiple callers can use different rq's.

Guaranteed live ->autogroup should be good enough for heuristic use, and had better be so. Having to take ->siglock in the fast path would kill using ->signal.

Yes, sure, rq->lock should ensure signal->autogroup can't go away. (even if it can be changed under us). And it does, we are moving all threads before kref_put().

Quoted 11 lines
kernel/sched_autogroup.c new file +11 -0
+static void
+autogroup_move_group(struct task_struct *p, struct autogroup *ag)
+{
+	struct autogroup *prev;
+	struct task_struct *t;
+
+	prev = p->signal->autogroup;
+	if (prev == ag)
+		return;
+
+	p->signal->autogroup = autogroup_kref_get(ag);

kernel/sched_autogroup.c line 108

Quoted 10 lines
kernel/sched_autogroup.c new file +10 -0
+	sched_move_task(p);
+
+	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);
+}

Well, this looks a bit strange (but correct).

We are changing ->autogroup assuming the caller holds ->siglock. But if we hold ->siglock we do not need rcu_read_lock() to iterate over the thread_group, we can just do

	p->signal->autogroup = autogroup_kref_get(ag);

	t = p;
	do {
		sched_move_task(t);
	} while_each_thread(p, t);

Again, this is minor, I won't insist.

Oleg.

#192 lore raw
Quoted 7 lines · Oleg Nesterov
Quoted 5 lines · Mike Galbraith
Quoted 2 lines · Oleg Nesterov

And the exiting task can do a lot before it disappears, probably we shouldn't ignore ->autogroup.

Quoted 6 lines

I don't really understand what makes the exiting task different, but OK.

However, I must admit I dislike this check. Because, looking at this code, it is not clear why do we check PF_EXITING. It looks as if it is needed for correctness.

Is _not_ needed I presume.

I'll remove it, I'm not overly attached (a t t a..;) to it.

Quoted 5 lines

OK, this is minor. I think the patch is correct, just one nit below.

Quoted 3 lines
Quoted 2 lines

It can't protect the change of signal->autogroup, multiple callers can use different rq's.

Quoted 7 lines
Quoted 3 lines

Guaranteed live ->autogroup should be good enough for heuristic use, and had better be so. Having to take ->siglock in the fast path would kill using ->signal.

Yes, sure, rq->lock should ensure signal->autogroup can't go away. (even if it can be changed under us). And it does, we are moving all threads before kref_put().

(yeah)

Quoted 15 lines
Quoted 15 lines
kernel/sched_autogroup.c new file +15 -0
+static void
+autogroup_move_group(struct task_struct *p, struct autogroup *ag)
+{
+	struct autogroup *prev;
+	struct task_struct *t;
+
+	prev = p->signal->autogroup;
+	if (prev == ag)
+		return;
+
+	p->signal->autogroup = autogroup_kref_get(ag);
+	sched_move_task(p);
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(t, &p->thread_group, thread_group) {
Quoted 8 lines

kernel/sched_autogroup.c line 108

Quoted 6 lines
kernel/sched_autogroup.c new file +6 -0
+		sched_move_task(t);
+	}
+	rcu_read_unlock();
+
+	autogroup_kref_put(prev);
+}

Well, this looks a bit strange (but correct).

My mouse copied it.

Quoted 4 lines

We are changing ->autogroup assuming the caller holds ->siglock. But if we hold ->siglock we do not need rcu_read_lock() to iterate over the thread_group, we can just do

Quoted 8 lines
	p->signal->autogroup = autogroup_kref_get(ag);

	t = p;
	do {
		sched_move_task(t);
	} while_each_thread(p, t);

Again, this is minor, I won't insist.

I'll do it that way. I was pondering adding the option to move one or all as cgroups does, but don't think that will ever be needed.

Thanks,

-Mike

#193 lore raw
Quoted 9 lines · Mike Galbraith
Quoted 3 lines · Oleg Nesterov

However, I must admit I dislike this check. Because, looking at this code, it is not clear why do we check PF_EXITING. It looks as if it is needed for correctness.

Is _not_ needed I presume.

I'll remove it, I'm not overly attached (a t t a..;) to it.

Argh!

I was wrong, it _is_ needed for correctness. Yes, it is always safe to read the pointer, but

Quoted 5 lines
Quoted 3 lines

Yes, sure, rq->lock should ensure signal->autogroup can't go away. (even if it can be changed under us). And it does, we are moving all threads before kref_put().

(yeah)

Exactly. And this means we can _only_ assume it can't go away if autogroup_move_group() can see us on ->thread_group list.

Perhaps this deserve a commen (unless I missed something again).

Mike, sorry for confusion.

Oleg.

#194 lore raw
Quoted 16 lines · Oleg Nesterov
Quoted 9 lines · Mike Galbraith
Quoted 3 lines · Oleg Nesterov

However, I must admit I dislike this check. Because, looking at this code, it is not clear why do we check PF_EXITING. It looks as if it is needed for correctness.

Is _not_ needed I presume.

I'll remove it, I'm not overly attached (a t t a..;) to it.

Argh!

I was wrong, it _is_ needed for correctness. Yes, it is always safe to read the pointer, but

Quoted 8 lines
Quoted 5 lines
Quoted 3 lines

Yes, sure, rq->lock should ensure signal->autogroup can't go away. (even if it can be changed under us). And it does, we are moving all threads before kref_put().

(yeah)

Exactly. And this means we can _only_ assume it can't go away if autogroup_move_group() can see us on ->thread_group list.

Aha!

Quoted 3 lines

Perhaps this deserve a commen (unless I missed something again).

Mike, sorry for confusion.

Oh no, thank you. I hadn't figured it out yet, was going to go back and poke rt kernel with sharp sticks. (exit can be one scary beast)

-Mike

#195 lore raw
Quoted 4 lines · Mike Galbraith
Quoted 2 lines

Exactly. And this means we can _only_ assume it can't go away if autogroup_move_group() can see us on ->thread_group list.

Aha!

Mike,

Mind sending a new patch with a separate v2 announcement in a new thread, once you have something i could apply to the scheduler tree (for a v2.6.38 merge)?

You sent a couple of iterations in this discussion and i'd rather not fish out the wrong one.

Thanks,

Ingo

#196 lore raw
Quoted 11 lines · Ingo Molnar
Quoted 4 lines · Mike Galbraith
Quoted 2 lines

Exactly. And this means we can _only_ assume it can't go away if autogroup_move_group() can see us on ->thread_group list.

Aha!

Mike,

Mind sending a new patch with a separate v2 announcement in a new thread, once you have something i could apply to the scheduler tree (for a v2.6.38 merge)?

Will do. (v3->936 became wider than expected;)

-Mike

#197 lore raw

PATCH v4 1/1 sched: automated per session task groups

In reply to Ingo Molnar
Quoted 4 lines · Ingo Molnar

Mike,

Mind sending a new patch with a separate v2 announcement in a new thread, once you have something i could apply to the scheduler tree (for a v2.6.38 merge)?

Changes since last:
- switch to per session vs tty
- make autogroups visible in /proc/sched_debug
- make autogroups visible in /proc/<pid>/autogroup
- add nice level bandwidth tweakability to /proc/<pid>/autogroup

Modulo "kill it" debate outcome...

A recurring complaint from CFS users is that parallel kbuild has a negative impact on desktop interactivity. This patch implements an idea from Linus, to automatically create task groups. This patch only per session autogroups, but leaves the way open for enhancement.

Implementation: each task's signal struct contains an inherited pointer to a refcounted autogroup struct containing a task group pointer, the default for all tasks pointing to the init_task_group. When a task calls setsid(), the process wide reference to the default group is dropped, a new task group is created, and the process is moved into the new task group. Children thereafter inherit this task group, and increase it's refcount. On exit, a reference to the current task group is dropped when the last reference to each signal struct is dropped. The task group is destroyed when the last signal struct referencing
it is freed. At runqueue selection time, IFF a task has no cgroup assignment,
it's current autogroup is used.

Autogroup bandwidth is controllable via setting it's nice level through the proc filesystem. cat /proc/<pid>/autogroup displays the task's group and the group's nice level. echo <nice level> > /proc/<pid>/autogroup sets the task group's shares to the weight of nice <level> task. Setting nice level is rate limited for !admin users due to the abuse risk of task group locking.

The feature is enabled from boot by default if CONFIG_SCHED_AUTOGROUP is selected, but can be disabled via the boot option noautogroup, and can be also be turned on/off on the fly via..
echo [01] > /proc/sys/kernel/sched_autogroup_enabled.
..which will automatically move tasks to/from the root task group.

---
 Documentation/kernel-parameters.txt |    2 
 fs/proc/base.c                      |   79 +++++++++++
 include/linux/sched.h               |   23 +++
 init/Kconfig                        |   12 +
 kernel/fork.c                       |    5 
 kernel/sched.c                      |   13 +
 kernel/sched_autogroup.c            |  243 ++++++++++++++++++++++++++++++++++++
 kernel/sched_autogroup.h            |   23 +++
 kernel/sched_debug.c                |   29 ++--
 kernel/sys.c                        |    4 
 kernel/sysctl.c                     |   11 +
 11 files changed, 426 insertions(+), 18 deletions(-)
include/linux/sched.h +23 -0
Index: linux-2.6.37.git/include/linux/sched.h
===================================================================
@@ -509,6 +509,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
@@ -576,6 +578,9 @@ 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.
@@ -1931,6 +1936,24 @@ 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);
+#ifdef CONFIG_PROC_FS
+extern void proc_sched_autogroup_show_task(struct task_struct *p, struct seq_file *m);
+extern int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice);
+#endif
+#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);
kernel/sched.c +11 -2
Index: linux-2.6.37.git/kernel/sched.c
===================================================================
@@ -78,6 +78,7 @@
 
 #include "sched_cpupri.h"
 #include "workqueue_sched.h"
+#include "sched_autogroup.h"
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/sched.h>
@@ -268,6 +269,10 @@ struct task_group {
 	struct task_group *parent;
 	struct list_head siblings;
 	struct list_head children;
+
+#ifdef CONFIG_SCHED_AUTOGROUP
+	struct autogroup *autogroup;
+#endif
 };
 
 #define root_task_group init_task_group
@@ -605,11 +610,14 @@ static inline int cpu_of(struct rq *rq)
  */
 static inline struct task_group *task_group(struct task_struct *p)
 {
+	struct task_group *tg;
 	struct cgroup_subsys_state *css;
 
 	css = task_subsys_state_check(p, cpu_cgroup_subsys_id,
 			lockdep_is_held(&task_rq(p)->lock));
-	return container_of(css, struct task_group, css);
+	tg = container_of(css, struct task_group, css);
+
+	return autogroup_task_group(p, tg);
 }
 
 /* Change a task's cfs_rq and parent entity if it moves across CPUs/groups */
@@ -2006,6 +2014,7 @@ static void sched_irq_time_avg_update(st
 #include "sched_idletask.c"
 #include "sched_fair.c"
 #include "sched_rt.c"
+#include "sched_autogroup.c"
 #include "sched_stoptask.c"
 #ifdef CONFIG_SCHED_DEBUG
 # include "sched_debug.c"
@@ -7979,7 +7988,7 @@ void __init sched_init(void)
 #ifdef CONFIG_CGROUP_SCHED
 	list_add(&init_task_group.list, &task_groups);
 	INIT_LIST_HEAD(&init_task_group.children);
-
+	autogroup_init(&init_task);
 #endif /* CONFIG_CGROUP_SCHED */
 
 #if defined CONFIG_FAIR_GROUP_SCHED && defined CONFIG_SMP
kernel/fork.c +4 -1
Index: linux-2.6.37.git/kernel/fork.c
===================================================================
@@ -174,8 +174,10 @@ static inline void free_signal_struct(st
 
 static inline void put_signal_struct(struct signal_struct *sig)
 {
-	if (atomic_dec_and_test(&sig->sigcnt))
+	if (atomic_dec_and_test(&sig->sigcnt)) {
+		sched_autogroup_exit(sig);
 		free_signal_struct(sig);
+	}
 }
 
 void __put_task_struct(struct task_struct *tsk)
@@ -904,6 +906,7 @@ static int copy_signal(unsigned long clo
 	posix_cpu_timers_init_group(sig);
 
 	tty_audit_fork(sig);
+	sched_autogroup_fork(sig);
 
 	sig->oom_adj = current->signal->oom_adj;
 	sig->oom_score_adj = current->signal->oom_score_adj;
kernel/sys.c +3 -1
Index: linux-2.6.37.git/kernel/sys.c
===================================================================
@@ -1080,8 +1080,10 @@ SYSCALL_DEFINE0(setsid)
 	err = session;
 out:
 	write_unlock_irq(&tasklist_lock);
-	if (err > 0)
+	if (err > 0) {
 		proc_sid_connector(group_leader);
+		sched_autogroup_create_attach(group_leader);
+	}
 	return err;
 }
 
kernel/sched_debug.c +15 -14
Index: linux-2.6.37.git/kernel/sched_debug.c
===================================================================
@@ -87,6 +87,20 @@ static void print_cfs_group_stats(struct
 }
 #endif
 
+#if defined(CONFIG_CGROUP_SCHED) && \
+	(defined(CONFIG_FAIR_GROUP_SCHED) || defined(CONFIG_RT_GROUP_SCHED))
+static void task_group_path(struct task_group *tg, char *buf, int buflen)
+{
+	/* may be NULL if the underlying cgroup isn't fully-created yet */
+	if (!tg->css.cgroup) {
+		if (!autogroup_path(tg, buf, buflen))
+			buf[0] = '\0';
+		return;
+	}
+	cgroup_path(tg->css.cgroup, buf, buflen);
+}
+#endif
+
 static void
 print_task(struct seq_file *m, struct rq *rq, struct task_struct *p)
 {
@@ -115,7 +129,7 @@ print_task(struct seq_file *m, struct rq
 		char path[64];
 
 		rcu_read_lock();
-		cgroup_path(task_group(p)->css.cgroup, path, sizeof(path));
+		task_group_path(task_group(p), path, sizeof(path));
 		rcu_read_unlock();
 		SEQ_printf(m, " %s", path);
 	}
@@ -147,19 +161,6 @@ static void print_rq(struct seq_file *m,
 	read_unlock_irqrestore(&tasklist_lock, flags);
 }
 
-#if defined(CONFIG_CGROUP_SCHED) && \
-	(defined(CONFIG_FAIR_GROUP_SCHED) || defined(CONFIG_RT_GROUP_SCHED))
-static void task_group_path(struct task_group *tg, char *buf, int buflen)
-{
-	/* may be NULL if the underlying cgroup isn't fully-created yet */
-	if (!tg->css.cgroup) {
-		buf[0] = '\0';
-		return;
-	}
-	cgroup_path(tg->css.cgroup, buf, buflen);
-}
-#endif
-
 void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq)
 {
 	s64 MIN_vruntime = -1, min_vruntime, max_vruntime = -1,
fs/proc/base.c +79 -0

91 lines not on this page · raw message

kernel/sched_autogroup.h new file +23 -0
Index: linux-2.6.37.git/kernel/sched_autogroup.h
===================================================================
@@ -0,0 +1,23 @@
+#ifdef CONFIG_SCHED_AUTOGROUP
+
+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;
+}
+
+#ifdef CONFIG_SCHED_DEBUG
+static inline int autogroup_path(struct task_group *tg, char *buf, int buflen)
+{
+	return 0;
+}
+#endif
+
+#endif /* CONFIG_SCHED_AUTOGROUP */
kernel/sched_autogroup.c new file +243 -0

243 lines not on this page · raw message

kernel/sysctl.c +11 -0
Index: linux-2.6.37.git/kernel/sysctl.c
===================================================================
@@ -382,6 +382,17 @@ static struct ctl_table kern_table[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec,
 	},
+#ifdef CONFIG_SCHED_AUTOGROUP
+	{
+		.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
 	{
 		.procname	= "prove_locking",
init/Kconfig +12 -0
Index: linux-2.6.37.git/init/Kconfig
===================================================================
@@ -728,6 +728,18 @@ config NET_NS
 
 endif # NAMESPACES
 
+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 session.
+
 config MM_OWNER
 	bool
 
Documentation/kernel-parameters.txt +2 -0
Index: linux-2.6.37.git/Documentation/kernel-parameters.txt
===================================================================
@@ -1622,6 +1622,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.
 
#198 lore raw

[tip:sched/core] sched: Add 'autogroup' scheduling feature: automated per session task groups

Commit-ID:  5091faa449ee0b7d73bc296a93bca9540fc51d0a
Gitweb:     http://git.kernel.org/tip/5091faa449ee0b7d73bc296a93bca9540fc51d0a
Author:     Mike Galbraith <[email protected]>
AuthorDate: Tue, 30 Nov 2010 14:18:03 +0100
Committer:  Ingo Molnar <[email protected]>
CommitDate: Tue, 30 Nov 2010 16:03:35 +0100

sched: Add 'autogroup' scheduling feature: automated per session task groups

A recurring complaint from CFS users is that parallel kbuild has a negative impact on desktop interactivity. This patch implements an idea from Linus, to automatically create task groups. Currently, only per session autogroups are implemented, but the patch leaves the way open for enhancement.

Implementation: each task's signal struct contains an inherited pointer to a refcounted autogroup struct containing a task group pointer, the default for all tasks pointing to the init_task_group. When a task calls setsid(), a new task group is created, the process is moved into the new task group, and a reference to the preveious task group is dropped. Child processes inherit this task group thereafter, and increase it's refcount. When the last thread of a process exits, the process's reference is dropped, such that when the last process referencing an autogroup exits, the autogroup is destroyed.

At runqueue selection time, IFF a task has no cgroup assignment, its current autogroup is used.

Autogroup bandwidth is controllable via setting it's nice level through the proc filesystem:

cat /proc/<pid>/autogroup

Displays the task's group and the group's nice level.

echo <nice level> > /proc/<pid>/autogroup

Sets the task group's shares to the weight of nice <level> task. Setting nice level is rate limited for !admin users due to the abuse risk of task group locking.

The feature is enabled from boot by default if CONFIG_SCHED_AUTOGROUP=y is selected, but can be disabled via the boot option noautogroup, and can also be turned on/off on the fly via:

echo [01] > /proc/sys/kernel/sched_autogroup_enabled

... which will automatically move tasks to/from the root task group.

Cc Markus Trippelsdorf <[email protected]>
Cc Mathieu Desnoyers <[email protected]>
Cc Paul Turner <[email protected]>
Cc Oleg Nesterov <[email protected]>
[ Removed the task_group_path() debug code, and fixed !EVENTFD build failure. ]

LKML-Reference: <[email protected]>

---
 Documentation/kernel-parameters.txt |    2 +
 fs/proc/base.c                      |   79 ++++++++++++
 include/linux/sched.h               |   23 ++++
 init/Kconfig                        |   13 ++
 kernel/fork.c                       |    5 +-
 kernel/sched.c                      |   13 ++-
 kernel/sched_autogroup.c            |  229 +++++++++++++++++++++++++++++++++++
 kernel/sched_autogroup.h            |   32 +++++
 kernel/sched_debug.c                |   47 +-------
 kernel/sys.c                        |    4 +-
 kernel/sysctl.c                     |   11 ++
 11 files changed, 409 insertions(+), 49 deletions(-)
Documentation/kernel-parameters.txt +2 -0
index 92e83e5..86820a7 100644
@@ -1622,6 +1622,8 @@ and is between 256 and 4096 characters. It is defined in the file
 	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.
 
fs/proc/base.c +79 -0

91 lines not on this page · raw message

include/linux/sched.h +23 -0
index a5b92c7..9c2d46d 100644
@@ -509,6 +509,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
@@ -576,6 +578,9 @@ 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.
@@ -1927,6 +1932,24 @@ int sched_rt_handler(struct ctl_table *table, int write,
 
 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);
+#ifdef CONFIG_PROC_FS
+extern void proc_sched_autogroup_show_task(struct task_struct *p, struct seq_file *m);
+extern int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice);
+#endif
+#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);
init/Kconfig +13 -0
index 88c1046..f1bba0a 100644
@@ -728,6 +728,19 @@ config NET_NS
 
 endif # NAMESPACES
 
+config SCHED_AUTOGROUP
+	bool "Automatic process group scheduling"
+	select EVENTFD
+	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 session.
+
 config MM_OWNER
 	bool
 
kernel/fork.c +4 -1
index 3b159c5..b6f2475 100644
@@ -174,8 +174,10 @@ static inline void free_signal_struct(struct signal_struct *sig)
 
 static inline void put_signal_struct(struct signal_struct *sig)
 {
-	if (atomic_dec_and_test(&sig->sigcnt))
+	if (atomic_dec_and_test(&sig->sigcnt)) {
+		sched_autogroup_exit(sig);
 		free_signal_struct(sig);
+	}
 }
 
 void __put_task_struct(struct task_struct *tsk)
@@ -904,6 +906,7 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
 	posix_cpu_timers_init_group(sig);
 
 	tty_audit_fork(sig);
+	sched_autogroup_fork(sig);
 
 	sig->oom_adj = current->signal->oom_adj;
 	sig->oom_score_adj = current->signal->oom_score_adj;
kernel/sched.c +11 -2
index 66ef579..b646dad 100644
@@ -79,6 +79,7 @@
 
 #include "sched_cpupri.h"
 #include "workqueue_sched.h"
+#include "sched_autogroup.h"
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/sched.h>
@@ -271,6 +272,10 @@ struct task_group {
 	struct task_group *parent;
 	struct list_head siblings;
 	struct list_head children;
+
+#ifdef CONFIG_SCHED_AUTOGROUP
+	struct autogroup *autogroup;
+#endif
 };
 
 #define root_task_group init_task_group
@@ -603,11 +608,14 @@ static inline int cpu_of(struct rq *rq)
  */
 static inline struct task_group *task_group(struct task_struct *p)
 {
+	struct task_group *tg;
 	struct cgroup_subsys_state *css;
 
 	css = task_subsys_state_check(p, cpu_cgroup_subsys_id,
 			lockdep_is_held(&task_rq(p)->lock));
-	return container_of(css, struct task_group, css);
+	tg = container_of(css, struct task_group, css);
+
+	return autogroup_task_group(p, tg);
 }
 
 /* Change a task's cfs_rq and parent entity if it moves across CPUs/groups */
@@ -1869,6 +1877,7 @@ static void sched_irq_time_avg_update(struct rq *rq, u64 curr_irq_time) { }
 #include "sched_idletask.c"
 #include "sched_fair.c"
 #include "sched_rt.c"
+#include "sched_autogroup.c"
 #include "sched_stoptask.c"
 #ifdef CONFIG_SCHED_DEBUG
 # include "sched_debug.c"
@@ -7750,7 +7759,7 @@ void __init sched_init(void)
 #ifdef CONFIG_CGROUP_SCHED
 	list_add(&init_task_group.list, &task_groups);
 	INIT_LIST_HEAD(&init_task_group.children);
-
+	autogroup_init(&init_task);
 #endif /* CONFIG_CGROUP_SCHED */
 
 	for_each_possible_cpu(i) {
kernel/sched_autogroup.c new file +229 -0

229 lines not on this page · raw message

kernel/sched_autogroup.h new file +32 -0
new file mode 100644
index 0000000..5358e24
@@ -0,0 +1,32 @@
+#ifdef CONFIG_SCHED_AUTOGROUP
+
+struct autogroup {
+	struct kref		kref;
+	struct task_group	*tg;
+	struct rw_semaphore	lock;
+	unsigned long		id;
+	int			nice;
+};
+
+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 void autogroup_free(struct task_group *tg) { }
+
+static inline struct task_group *
+autogroup_task_group(struct task_struct *p, struct task_group *tg)
+{
+	return tg;
+}
+
+#ifdef CONFIG_SCHED_DEBUG
+static inline int autogroup_path(struct task_group *tg, char *buf, int buflen)
+{
+	return 0;
+}
+#endif
+
+#endif /* CONFIG_SCHED_AUTOGROUP */
kernel/sched_debug.c +2 -45

85 lines not on this page · raw message

kernel/sys.c +3 -1
index 7f5a0cd..2745dcd 100644
@@ -1080,8 +1080,10 @@ SYSCALL_DEFINE0(setsid)
 	err = session;
 out:
 	write_unlock_irq(&tasklist_lock);
-	if (err > 0)
+	if (err > 0) {
 		proc_sid_connector(group_leader);
+		sched_autogroup_create_attach(group_leader);
+	}
 	return err;
 }
 
kernel/sysctl.c +11 -0
index a00fdef..121e4ff 100644
@@ -370,6 +370,17 @@ static struct ctl_table kern_table[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec,
 	},
+#ifdef CONFIG_SCHED_AUTOGROUP
+	{
+		.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
 	{
 		.procname	= "prove_locking",
#199 lore raw

Re: [tip:sched/core] sched: Add 'autogroup' scheduling feature: automated per session task groups

Sorry for the late reply, I am slowly crawling through my mbox...

Quoted 7 lines · tip-bot for Mike Galbraith
Commit-ID:  5091faa449ee0b7d73bc296a93bca9540fc51d0a
Gitweb:     http://git.kernel.org/tip/5091faa449ee0b7d73bc296a93bca9540fc51d0a
Author:     Mike Galbraith <[email protected]>
AuthorDate: Tue, 30 Nov 2010 14:18:03 +0100
Committer:  Ingo Molnar <[email protected]>
CommitDate: Tue, 30 Nov 2010 16:03:35 +0100

I assume this is the latest version. In this case I think it needs minor fixes.

Review

Quoted 3 lines
(quoted diff) +3 -0
+#ifdef CONFIG_PROC_FS
+
+/* Called with siglock held. */

This is not true, and that is why we can't blindly use kref_get().

Quoted 12 lines
(quoted diff) +12 -0
+int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice)
+{
+	static unsigned long next = INITIAL_JIFFIES;
+	struct autogroup *ag;
+	int err;
+
+	if (*nice < -20 || *nice > 19)
+		return -EINVAL;
+
+	err = security_task_setnice(current, *nice);
+	if (err)
+		return err;

Review

Quoted 10 lines
(quoted diff) +10 -0
+
+	if (*nice < 0 && !can_nice(current, *nice))
+		return -EPERM;
+
+	/* this is a heavy operation taking global locks.. */
+	if (!capable(CAP_SYS_ADMIN) && time_before(jiffies, next))
+		return -EAGAIN;
+
+	next = HZ / 10 + jiffies;
+	ag = autogroup_kref_get(p->signal->autogroup);

We can race with autogroup_move_group() and use the already freed ->autogroup. We need ->siglock or task_rq_lock() to read it.

IOW, I think we need something like the patch below, but - sorry - if was completely untested.

And the question,

Review

Quoted 2 lines
(quoted diff) +2 -0
+	down_write(&ag->lock);
+	err = sched_group_set_shares(ag->tg, prio_to_weight[*nice + 20]);

Do we really want this if ag == autogroup_default ? Say, autogroup_create() fails, now the owner of this process can affect init_task_group. Or admin can change init_task_group "by accident" (although currently this is hardly possible, sched_autogroup_detach() has no callers). Just curious.

Oleg.

kernel/sched_autogroup.c +22 -9
@@ -41,6 +41,19 @@ static inline struct autogroup *autogrou
 	return ag;
 }
 
+static struct autogroup *task_get_autogroup(struct task_struct *p)
+{
+	struct autogroup *ag = NULL;
+	unsigned long flags;
+
+	if (lock_task_sighand(p, &flags)) {
+		ag = autogroup_kref_get(p->signal->autogroup);
+		unlock_task_sighand(p, &flags);
+	}
+
+	return ag;
+}
+
 static inline struct autogroup *autogroup_create(void)
 {
 	struct autogroup *ag = kzalloc(sizeof(*ag), GFP_KERNEL);
@@ -149,11 +162,7 @@ EXPORT_SYMBOL(sched_autogroup_detach);
 
 void sched_autogroup_fork(struct signal_struct *sig)
 {
-	struct task_struct *p = current;
-
-	spin_lock_irq(&p->sighand->siglock);
-	sig->autogroup = autogroup_kref_get(p->signal->autogroup);
-	spin_unlock_irq(&p->sighand->siglock);
+	sig->autogroup = task_get_autogroup(current);
 }
 
 void sched_autogroup_exit(struct signal_struct *sig)
@@ -172,7 +181,6 @@ __setup("noautogroup", setup_autogroup);
 
 #ifdef CONFIG_PROC_FS
 
-/* Called with siglock held. */
 int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice)
 {
 	static unsigned long next = INITIAL_JIFFIES;
@@ -193,9 +201,11 @@ int proc_sched_autogroup_set_nice(struct
 	if (!capable(CAP_SYS_ADMIN) && time_before(jiffies, next))
 		return -EAGAIN;
 
-	next = HZ / 10 + jiffies;
-	ag = autogroup_kref_get(p->signal->autogroup);
+	ag = task_get_autogroup(p);
+	if (!ag)
+		return -ESRCH;
 
+	next = HZ / 10 + jiffies;
 	down_write(&ag->lock);
 	err = sched_group_set_shares(ag->tg, prio_to_weight[*nice + 20]);
 	if (!err)
@@ -209,7 +219,10 @@ int proc_sched_autogroup_set_nice(struct
 
 void proc_sched_autogroup_show_task(struct task_struct *p, struct seq_file *m)
 {
-	struct autogroup *ag = autogroup_kref_get(p->signal->autogroup);
+	struct autogroup *ag = task_get_autogroup(p);
+
+	if (!ag)
+		return;
 
 	down_read(&ag->lock);
 	seq_printf(m, "/autogroup-%ld nice %d\n", ag->id, ag->nice);
#200 lore raw

Re: [tip:sched/core] sched: Add 'autogroup' scheduling feature: automated per session task groups

Quoted 8 lines · Oleg Nesterov

I assume this is the latest version. In this case I think it needs minor fixes.

Review

Quoted 3 lines
(quoted diff) +3 -0
+#ifdef CONFIG_PROC_FS
+
+/* Called with siglock held. */

This is not true, and that is why we can't blindly use kref_get().

I was going to lock it all up, but convinced myself it wasn't necessary. The comment should have also gone away.

Quoted 17 lines
Quoted 17 lines
(quoted diff) +17 -0
+int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice)
+{
+	static unsigned long next = INITIAL_JIFFIES;
+	struct autogroup *ag;
+	int err;
+
+	if (*nice < -20 || *nice > 19)
+		return -EINVAL;
+
+	err = security_task_setnice(current, *nice);
+	if (err)
+		return err;
+
+	if (*nice < 0 && !can_nice(current, *nice))
+		return -EPERM;
+
+	/* this is a heavy operation taking global locks.. */
Quoted 8 lines

Review

Quoted 5 lines
(quoted diff) +5 -0
+	if (!capable(CAP_SYS_ADMIN) && time_before(jiffies, next))
+		return -EAGAIN;
+
+	next = HZ / 10 + jiffies;
+	ag = autogroup_kref_get(p->signal->autogroup);

We can race with autogroup_move_group() and use the already freed ->autogroup. We need ->siglock or task_rq_lock() to read it.

I don't see how/why. I took a reference to the new group before assignment of p->signal->autogroup, and put the previous group after it's assigned.

Ponders that.. uhoh.

Mover does atomic write, but signal->autogroup write comes after that, so can still be in flight when reader dereferences. Game over unless the reader beats ->autogroup writer to the punch.

Thanks again for your excellent eyeballs. The below should plug that hole, no? (hope so, seems pointless to lock movement)

Quoted 5 lines

IOW, I think we need something like the patch below, but - sorry - if was completely untested.

And the question,

Quoted 7 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+	down_write(&ag->lock);
+	err = sched_group_set_shares(ag->tg, prio_to_weight[*nice + 20]);

Do we really want this if ag == autogroup_default ? Say, autogroup_create() fails, now the owner of this process can affect init_task_group. Or admin can change init_task_group "by accident" (although currently this is hardly possible, sched_autogroup_detach() has no callers). Just curious.

sched_group_set_shares() does the right thing, says no to changing the root task group's shares.

sched: fix potential access to freed memory

Oleg pointed out that the /proc interface kref_get() useage may race with the final put during autogroup_move_group(). A signal->autogroup assignment may be in flight when the /proc interface dereference, leaving them taking a reference to an already dead group.

kernel/sched_autogroup.c +9 -3
index 57a7ac2..713b6c0 100644
@@ -41,6 +41,12 @@ static inline struct autogroup *autogroup_kref_get(struct autogroup *ag)
 	return ag;
 }
 
+static inline struct autogroup *autogroup_task_get(struct task_struct *p)
+{
+	smp_rmb();
+	return autogroup_kref_get(p->signal->autogroup);
+}
+
 static inline struct autogroup *autogroup_create(void)
 {
 	struct autogroup *ag = kzalloc(sizeof(*ag), GFP_KERNEL);
@@ -119,6 +125,7 @@ autogroup_move_group(struct task_struct *p, struct autogroup *ag)
 	}
 
 	p->signal->autogroup = autogroup_kref_get(ag);
+	smp_mb();
 
 	t = p;
 	do {
@@ -172,7 +179,6 @@ __setup("noautogroup", setup_autogroup);
 
 #ifdef CONFIG_PROC_FS
 
-/* Called with siglock held. */
 int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice)
 {
 	static unsigned long next = INITIAL_JIFFIES;
@@ -194,7 +200,7 @@ int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice)
 		return -EAGAIN;
 
 	next = HZ / 10 + jiffies;
-	ag = autogroup_kref_get(p->signal->autogroup);
+	ag = autogroup_task_get(p);
 
 	down_write(&ag->lock);
 	err = sched_group_set_shares(ag->tg, prio_to_weight[*nice + 20]);
@@ -209,7 +215,7 @@ int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice)
 
 void proc_sched_autogroup_show_task(struct task_struct *p, struct seq_file *m)
 {
-	struct autogroup *ag = autogroup_kref_get(p->signal->autogroup);
+	struct autogroup *ag = autogroup_task_get(p);
 
 	down_read(&ag->lock);
 	seq_printf(m, "/autogroup-%ld nice %d\n", ag->id, ag->nice);
#201 lore raw

Re: [tip:sched/core] sched: Add 'autogroup' scheduling feature: automated per session task groups

Quoted 4 lines · Mike Galbraith

On Wed, 2010-12-15 at 18:50 +0100, Oleg Nesterov wrote:

Thanks again for your excellent eyeballs. The below should plug that hole, no? (hope so, seems pointless to lock movement)

I'd also have to disable interrupts though, so may as well just lock it.

I didn't do the -ESRCH or no display bit. As far as autogroup is concerned, if you couldn't lock, it's history, so belongs to init.

sched: fix potential access to freed memory

Oleg pointed out that the /proc interface kref_get() useage may race with the final put during autogroup_move_group(). A signal->autogroup assignment may be in flight when the /proc interface dereference, leaving them taking a reference to an already dead group.

kernel/sched_autogroup.c +17 -8
index 57a7ac2..c80fedc 100644
@@ -41,6 +41,20 @@ static inline struct autogroup *autogroup_kref_get(struct autogroup *ag)
 	return ag;
 }
 
+static inline struct autogroup *autogroup_task_get(struct task_struct *p)
+{
+	struct autogroup *ag;
+	unsigned long flags;
+
+	if (!lock_task_sighand(p, &flags))
+		return autogroup_kref_get(&autogroup_default);
+
+	ag = autogroup_kref_get(p->signal->autogroup);
+	unlock_task_sighand(p, &flags);
+
+	return ag;
+}
+
 static inline struct autogroup *autogroup_create(void)
 {
 	struct autogroup *ag = kzalloc(sizeof(*ag), GFP_KERNEL);
@@ -149,11 +163,7 @@ EXPORT_SYMBOL(sched_autogroup_detach);
 
 void sched_autogroup_fork(struct signal_struct *sig)
 {
-	struct task_struct *p = current;
-
-	spin_lock_irq(&p->sighand->siglock);
-	sig->autogroup = autogroup_kref_get(p->signal->autogroup);
-	spin_unlock_irq(&p->sighand->siglock);
+	sig->autogroup = autogroup_task_get(current);
 }
 
 void sched_autogroup_exit(struct signal_struct *sig)
@@ -172,7 +182,6 @@ __setup("noautogroup", setup_autogroup);
 
 #ifdef CONFIG_PROC_FS
 
-/* Called with siglock held. */
 int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice)
 {
 	static unsigned long next = INITIAL_JIFFIES;
@@ -194,7 +203,7 @@ int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice)
 		return -EAGAIN;
 
 	next = HZ / 10 + jiffies;
-	ag = autogroup_kref_get(p->signal->autogroup);
+	ag = autogroup_task_get(p);
 
 	down_write(&ag->lock);
 	err = sched_group_set_shares(ag->tg, prio_to_weight[*nice + 20]);
@@ -209,7 +218,7 @@ int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice)
 
 void proc_sched_autogroup_show_task(struct task_struct *p, struct seq_file *m)
 {
-	struct autogroup *ag = autogroup_kref_get(p->signal->autogroup);
+	struct autogroup *ag = autogroup_task_get(p);
 
 	down_read(&ag->lock);
 	seq_printf(m, "/autogroup-%ld nice %d\n", ag->id, ag->nice);
#202 lore raw

Re: [tip:sched/core] sched: Add 'autogroup' scheduling feature: automated per session task groups

Quoted 2 lines · Mike Galbraith

I'd also have to disable interrupts though,

Even irq_disable can't help, I think.

Quoted 1 line

so may as well just lock it.

this also looks simpler.

Quoted 2 lines

I didn't do the -ESRCH or no display bit. As far as autogroup is concerned, if you couldn't lock, it's history, so belongs to init.

Agreed. I considered this option too, but I was worried about sched_group_set_shares(root).

However, as you pointed out,

Quoted 2 lines

sched_group_set_shares() does the right thing, says no to changing the root task group's shares.

Aha, I see, thanks.

I believe the patch is correct and closes the hole.

Oleg.

#203 lore raw

[tip:sched/core] sched, autogroup: Fix potential access to freed memory

In reply to Mike Galbraith
Commit-ID:  4f8219875a0dad2cfad9e93a3fafcd9626db98d2
Gitweb:     http://git.kernel.org/tip/4f8219875a0dad2cfad9e93a3fafcd9626db98d2
Author:     Mike Galbraith <[email protected]>
AuthorDate: Thu, 16 Dec 2010 15:09:52 +0100
Committer:  Ingo Molnar <[email protected]>
CommitDate: Tue, 4 Jan 2011 15:10:34 +0100

sched, autogroup: Fix potential access to freed memory

Oleg pointed out that the /proc interface kref_get() useage may race with the final put during autogroup_move_group(). A signal->autogroup assignment may be in flight when the /proc interface dereference, leaving them taking a reference to an already dead group.

LKML-Reference: <[email protected]>

---
 kernel/sched_autogroup.c |   25 +++++++++++++++++--------
 1 files changed, 17 insertions(+), 8 deletions(-)
kernel/sched_autogroup.c +17 -8
index 57a7ac2..c80fedc 100644
@@ -41,6 +41,20 @@ static inline struct autogroup *autogroup_kref_get(struct autogroup *ag)
 	return ag;
 }
 
+static inline struct autogroup *autogroup_task_get(struct task_struct *p)
+{
+	struct autogroup *ag;
+	unsigned long flags;
+
+	if (!lock_task_sighand(p, &flags))
+		return autogroup_kref_get(&autogroup_default);
+
+	ag = autogroup_kref_get(p->signal->autogroup);
+	unlock_task_sighand(p, &flags);
+
+	return ag;
+}
+
 static inline struct autogroup *autogroup_create(void)
 {
 	struct autogroup *ag = kzalloc(sizeof(*ag), GFP_KERNEL);
@@ -149,11 +163,7 @@ EXPORT_SYMBOL(sched_autogroup_detach);
 
 void sched_autogroup_fork(struct signal_struct *sig)
 {
-	struct task_struct *p = current;
-
-	spin_lock_irq(&p->sighand->siglock);
-	sig->autogroup = autogroup_kref_get(p->signal->autogroup);
-	spin_unlock_irq(&p->sighand->siglock);
+	sig->autogroup = autogroup_task_get(current);
 }
 
 void sched_autogroup_exit(struct signal_struct *sig)
@@ -172,7 +182,6 @@ __setup("noautogroup", setup_autogroup);
 
 #ifdef CONFIG_PROC_FS
 
-/* Called with siglock held. */
 int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice)
 {
 	static unsigned long next = INITIAL_JIFFIES;
@@ -194,7 +203,7 @@ int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice)
 		return -EAGAIN;
 
 	next = HZ / 10 + jiffies;
-	ag = autogroup_kref_get(p->signal->autogroup);
+	ag = autogroup_task_get(p);
 
 	down_write(&ag->lock);
 	err = sched_group_set_shares(ag->tg, prio_to_weight[*nice + 20]);
@@ -209,7 +218,7 @@ int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice)
 
 void proc_sched_autogroup_show_task(struct task_struct *p, struct seq_file *m)
 {
-	struct autogroup *ag = autogroup_kref_get(p->signal->autogroup);
+	struct autogroup *ag = autogroup_task_get(p);
 
 	down_read(&ag->lock);
 	seq_printf(m, "/autogroup-%ld nice %d\n", ag->id, ag->nice);
#204 lore raw

Re: [tip:sched/core] sched: Add 'autogroup' scheduling feature: automated per session task groups

In reply to tip-bot for Mike Galbraith
Quoted 91 lines · tip-bot for Mike Galbraith

Commit-ID:  5091faa449ee0b7d73bc296a93bca9540fc51d0a
Gitweb:     http://git.kernel.org/tip/5091faa449ee0b7d73bc296a93bca9540fc51d0a
Author:     Mike Galbraith <[email protected]>
AuthorDate: Tue, 30 Nov 2010 14:18:03 +0100
Committer:  Ingo Molnar <[email protected]>
CommitDate: Tue, 30 Nov 2010 16:03:35 +0100

sched: Add 'autogroup' scheduling feature: automated per session task groups

kernel/sched_debug.c +2 -36
index e95b774..1dfae3d 100644
@@ -54,8 +54,7 @@ static unsigned long nsec_low(unsigned long long nsec)
  #define SPLIT_NS(x) nsec_high(x), nsec_low(x)
 
  #ifdef CONFIG_FAIR_GROUP_SCHED
-static void print_cfs_group_stats(struct seq_file *m, int cpu,
-               struct task_group *tg)
+static void print_cfs_group_stats(struct seq_file *m, int cpu, struct task_group *tg)
  {
        struct sched_entity *se = tg->se[cpu];
        if (!se)
@@ -110,16 +109,6 @@ print_task(struct seq_file *m, struct rq *rq, struct task_struct *p)
                0LL, 0LL, 0LL, 0L, 0LL, 0L, 0LL, 0L);
  #endif
 
-#ifdef CONFIG_CGROUP_SCHED
-       {
-               char path[64];
-
-               rcu_read_lock();
-               cgroup_path(task_group(p)->css.cgroup, path, sizeof(path));
-               rcu_read_unlock();
-               SEQ_printf(m, " %s", path);
-       }
-#endif
        SEQ_printf(m, "\n");
  }
 
@@ -147,19 +136,6 @@ static void print_rq(struct seq_file *m, struct rq *rq, int rq_cpu)
        read_unlock_irqrestore(&tasklist_lock, flags);
  }
 
-#if defined(CONFIG_CGROUP_SCHED) && \
-       (defined(CONFIG_FAIR_GROUP_SCHED) || defined(CONFIG_RT_GROUP_SCHED))
-static void task_group_path(struct task_group *tg, char *buf, int buflen)
-{
-       /* may be NULL if the underlying cgroup isn't fully-created yet */
-       if (!tg->css.cgroup) {
-               buf[0] = '\0';
-               return;
-       }
-       cgroup_path(tg->css.cgroup, buf, buflen);
-}
-#endif
-
  void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq)
  {
        s64 MIN_vruntime = -1, min_vruntime, max_vruntime = -1,
@@ -168,16 +144,7 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq)
        struct sched_entity *last;
        unsigned long flags;
 
-#if defined(CONFIG_CGROUP_SCHED) && defined(CONFIG_FAIR_GROUP_SCHED)
-       char path[128];
-       struct task_group *tg = cfs_rq->tg;
-
-       task_group_path(tg, path, sizeof(path));
-
-       SEQ_printf(m, "\ncfs_rq[%d]:%s\n", cpu, path);
-#else
        SEQ_printf(m, "\ncfs_rq[%d]:\n", cpu);
-#endif
        SEQ_printf(m, "  .%-30s: %Ld.%06ld\n", "exec_clock",
                        SPLIT_NS(cfs_rq->exec_clock));
 
@@ -215,7 +182,7 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq)
        SEQ_printf(m, "  .%-30s: %ld\n", "load_contrib",
                        cfs_rq->load_contribution);
        SEQ_printf(m, "  .%-30s: %d\n", "load_tg",
-                       atomic_read(&tg->load_weight));
+                       atomic_read(&cfs_rq->tg->load_weight));
  #endif
 
        print_cfs_group_stats(m, cpu, cfs_rq->tg);
@@ -224,17 +191,7 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq)
 
  void print_rt_rq(struct seq_file *m, int cpu, struct rt_rq *rt_rq)
  {
-#if defined(CONFIG_CGROUP_SCHED) && defined(CONFIG_RT_GROUP_SCHED)

kernel/sched_debug.c void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) · line 237

Quoted 10 lines
kernel/sched_debug.c +0 -9
-       char path[128];
-       struct task_group *tg = rt_rq->tg;
-
-       task_group_path(tg, path, sizeof(path));
-
-       SEQ_printf(m, "\nrt_rq[%d]:%s\n", cpu, path);
-#else
        SEQ_printf(m, "\nrt_rq[%d]:\n", cpu);
-#endif
-

The above change as well as the recent changes due to tg_shares_up improvements have two (undesirable ?) side effects on /proc/sched_debug output.

The autogroup patchset removes the display of cgroup name from sched_debug output.

On a 16 CPU system, with 2 groups having one task each and one task in root group, the difference in o/p appears like this:

$ grep while1 sched_debug-2.6.37-rc5
R         while1  2208     13610.855787      1960   120
13610.855787     19272.857661         0.000000 /2
R         while1  2207     20255.605110      3160   120
20255.605110     31572.634065         0.000000 /1
R         while1  2209     63913.721827      1273   120
63913.721827     12604.411880         0.000000 /
$ grep while1 sched_debug-2.6.37-rc5-tip
R         while1  2173     17603.479529      2754   120
17603.479529     25818.279858         4.560010
R         while1  2174     11435.667691      1669   120
11435.667691     16456.476663         0.000000
R         while1  2175     10074.709060      1019   120
10074.709060     10075.915495         0.000000

So you can see in the latter case, it becomes difficult to see which task belongs to which group.

The group names are also missing from per-CPU rq information. Hence in the above example of 2 groups, I see 2 blocks of data for 2 cfs_rqs, but it is not possible to know which group they represent.

Also, with tg_shares_up improvements, the leaf cfs_rqs are maintained on rq->leaf_cfs_rq_list only if they carry any load. But the code to display cfs_rq information for sched_debug isn't updated and hence information from a few cfs_rqs are missing from sched_debug.

$ grep cfs_rq sched_debug-2.6.37-rc5
cfs_rq[0]:/2
cfs_rq[0]:/1
cfs_rq[0]:/
cfs_rq[1]:/2
cfs_rq[1]:/1
cfs_rq[1]:/
cfs_rq[2]:/2
cfs_rq[2]:/1
cfs_rq[2]:/
cfs_rq[3]:/2
cfs_rq[3]:/1
cfs_rq[3]:/
cfs_rq[4]:/2
cfs_rq[4]:/1
cfs_rq[4]:/
cfs_rq[5]:/2
cfs_rq[5]:/1
cfs_rq[5]:/
cfs_rq[6]:/2
cfs_rq[6]:/1
cfs_rq[6]:/
cfs_rq[7]:/2
cfs_rq[7]:/1
cfs_rq[7]:/
cfs_rq[8]:/2
cfs_rq[8]:/1
cfs_rq[8]:/
cfs_rq[9]:/2
cfs_rq[9]:/1
cfs_rq[9]:/
cfs_rq[10]:/2
cfs_rq[10]:/1
cfs_rq[10]:/
cfs_rq[11]:/2
cfs_rq[11]:/1
cfs_rq[11]:/
cfs_rq[12]:/2
cfs_rq[12]:/1
cfs_rq[12]:/
cfs_rq[13]:/2
cfs_rq[13]:/1
cfs_rq[13]:/
cfs_rq[14]:/2
cfs_rq[14]:/1
cfs_rq[14]:/
cfs_rq[15]:/2
cfs_rq[15]:/1
cfs_rq[15]:/
$ grep cfs_rq sched_debug-2.6.37-rc5-tip
cfs_rq[0]:
cfs_rq[0]:
cfs_rq[1]:
cfs_rq[2]:
cfs_rq[3]:
cfs_rq[4]:
cfs_rq[4]:
cfs_rq[5]:
cfs_rq[5]:
cfs_rq[6]:
cfs_rq[7]:
cfs_rq[8]:
cfs_rq[9]:
cfs_rq[10]:
cfs_rq[11]:
cfs_rq[11]:
cfs_rq[12]:
cfs_rq[12]:
cfs_rq[13]:
cfs_rq[13]:
cfs_rq[14]:
cfs_rq[14]:
cfs_rq[15]:
cfs_rq[15]:

Regards,
Bharata.

#205 lore raw

Re: [tip:sched/core] sched: Add 'autogroup' scheduling feature: automated per session task groups

Quoted 2 lines · Bharata B Rao

The autogroup patchset removes the display of cgroup name from sched_debug output.

Hrmph.. that wasn't supposed to happen, care to send a patch to fix that up?

Quoted 4 lines

Also, with tg_shares_up improvements, the leaf cfs_rqs are maintained on rq->leaf_cfs_rq_list only if they carry any load. But the code to display cfs_rq information for sched_debug isn't updated and hence information from a few cfs_rqs are missing from sched_debug.

Well, that's a _good_ thing, right?

I mean, if we know they're empty, and don't contribute to schedule, why bother displaying them?

#206 lore raw

Re: [tip:sched/core] sched: Add 'autogroup' scheduling feature: automated per session task groups

Quoted 6 lines · Peter Zijlstra
Quoted 2 lines · Bharata B Rao

The autogroup patchset removes the display of cgroup name from sched_debug output.

Hrmph.. that wasn't supposed to happen, care to send a patch to fix that up?

There are two aspects here:

- Printing cgroup name for per-CPU cfs_rqs shouldn't be affected by autogroup and the old code should work here.
- Printing cgroup name for tasks depends on task_group(), which has been changed by autogroup patch. I haven't really looked deep into autogroup patch, but from whatever I can gather, Mike had a reason to remove this bit from sched_debug. The task groups created for autogroups don't have cgroups associated with them and hence no dentries and hence no pathnames.

I guess we could do fix this as shown in the attached patch.

Quoted 11 lines
Quoted 6 lines

Also, with tg_shares_up improvements, the leaf cfs_rqs are maintained on rq->leaf_cfs_rq_list only if they carry any load. But the code to display cfs_rq information for sched_debug isn't updated and hence information from a few cfs_rqs are missing from sched_debug.

Well, that's a _good_ thing, right?

I mean, if we know they're empty, and don't contribute to schedule, why bother displaying them?

In addition to tasks, we do display other details pertaining to the cfs_rq. I thought, having a complete view of all the cfs_rqs in the system would be better and consistent than obtaining different cfs_rqs on different captures of /proc/sched_debug.

Regards,
Bharata.

Signature · 1 line
http://bharata.sulekha.com/blog/posts.htm, http://raobharata.wordpress.com/

Re-instate cgroup names in /proc/sched_debug.

Autogroup patch removed displaying the cgroup pathnames for per-CPU cfs_rqs and tasks in /proc/sched_debug. Re-instate this.

kernel/sched_debug.c +51 -0
Index: linux-2.6-tip/kernel/sched_debug.c
===================================================================
@@ -86,6 +86,25 @@
 }
 #endif
 
+#if defined(CONFIG_CGROUP_SCHED) && \
+	(defined(CONFIG_FAIR_GROUP_SCHED) || defined(CONFIG_RT_GROUP_SCHED))
+/*
+ * This is essentially same as sched.c:task_group except
+ * that it doesn't bother about autogroups. Essentially
+ * this is how task_group() looked like before autogroup
+ * changes.
+ * Named task_sched_group() for want of a better name.
+ */
+static struct task_group *task_sched_group(struct task_struct *p)
+{
+	struct cgroup_subsys_state *css;
+
+	css = task_subsys_state_check(p, cpu_cgroup_subsys_id,
+			lockdep_is_held(&task_rq(p)->lock));
+	return container_of(css, struct task_group, css);
+}
+#endif
+
 static void
 print_task(struct seq_file *m, struct rq *rq, struct task_struct *p)
 {
@@ -108,6 +127,16 @@
 	SEQ_printf(m, "%15Ld %15Ld %15Ld.%06ld %15Ld.%06ld %15Ld.%06ld",
 		0LL, 0LL, 0LL, 0L, 0LL, 0L, 0LL, 0L);
 #endif
+#ifdef CONFIG_CGROUP_SCHED
+	{
+		char path[64];
+
+		rcu_read_lock();
+		cgroup_path(task_sched_group(p)->css.cgroup, path, sizeof(path));
+		rcu_read_unlock();
+		SEQ_printf(m, " %s", path);
+	}
+#endif
 
 	SEQ_printf(m, "\n");
 }
@@ -136,6 +165,19 @@
 	read_unlock_irqrestore(&tasklist_lock, flags);
 }
 
+#if defined(CONFIG_CGROUP_SCHED) && \
+	(defined(CONFIG_FAIR_GROUP_SCHED) || defined(CONFIG_RT_GROUP_SCHED))
+static void task_group_path(struct task_group *tg, char *buf, int buflen)
+{
+	/* may be NULL if the underlying cgroup isn't fully-created yet */
+	if (!tg->css.cgroup) {
+		buf[0] = '\0';
+		return;
+	}
+	cgroup_path(tg->css.cgroup, buf, buflen);
+}
+#endif
+
 void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq)
 {
 	s64 MIN_vruntime = -1, min_vruntime, max_vruntime = -1,
@@ -144,7 +186,16 @@
 	struct sched_entity *last;
 	unsigned long flags;
 
+#if defined(CONFIG_CGROUP_SCHED) && defined(CONFIG_FAIR_GROUP_SCHED)
+	char path[128];
+	struct task_group *tg = cfs_rq->tg;
+
+	task_group_path(tg, path, sizeof(path));
+
+	SEQ_printf(m, "\ncfs_rq[%d]:%s\n", cpu, path);
+#else
 	SEQ_printf(m, "\ncfs_rq[%d]:\n", cpu);
+#endif
 	SEQ_printf(m, "  .%-30s: %Ld.%06ld\n", "exec_clock",
 			SPLIT_NS(cfs_rq->exec_clock));
 
#207 lore raw

Re: [tip:sched/core] sched: Add 'autogroup' scheduling feature: automated per session task groups

Quoted 19 lines · Bharata B Rao
Quoted 6 lines · Peter Zijlstra
Quoted 2 lines · Bharata B Rao

The autogroup patchset removes the display of cgroup name from sched_debug output.

Hrmph.. that wasn't supposed to happen, care to send a patch to fix that up?

There are two aspects here:

- Printing cgroup name for per-CPU cfs_rqs shouldn't be affected by autogroup and the old code should work here.
- Printing cgroup name for tasks depends on task_group(), which has been changed by autogroup patch. I haven't really looked deep into autogroup patch, but from whatever I can gather, Mike had a reason to remove this bit from sched_debug. The task groups created for autogroups don't have cgroups associated with them and hence no dentries and hence no pathnames.

Quoted 1 line

I guess we could do fix this as shown in the attached patch.

I missed adding the similar bits to RT in sched_dubug.c. If this approach is reasonable, I can send the next one with RT changes included.

Regards,
Bharata.

#208 lore raw

Re: [tip:sched/core] sched: Add 'autogroup' scheduling feature: automated per session task groups

Quoted 4 lines · Bharata B Rao

I missed adding the similar bits to RT in sched_dubug.c. If this approach is reasonable, I can send the next one with RT changes included.

No that code needs a serious cleanup, I think you even (re-)introduced a NULL pointer deref (but I didn't look too closely).

Simply re-instating the code that was removed isn't sufficient.

Just write your patch as if its a new feature (and never even look at the old code), you'll very probably end up with a much saner patch.

#209 lore raw

Re: [tip:sched/core] sched: Add 'autogroup' scheduling feature: automated per session task groups

In reply to Bharata B Rao
Quoted 10 lines · Bharata B Rao
Quoted 6 lines · Peter Zijlstra
Quoted 2 lines · Bharata B Rao

The autogroup patchset removes the display of cgroup name from sched_debug output.

Hrmph.. that wasn't supposed to happen, care to send a patch to fix that up?

There are two aspects here:

Quoted 8 lines

- Printing cgroup name for per-CPU cfs_rqs shouldn't be affected by autogroup and the old code should work here.
- Printing cgroup name for tasks depends on task_group(), which has been changed by autogroup patch. I haven't really looked deep into autogroup patch, but from whatever I can gather, Mike had a reason to remove this bit from sched_debug. The task groups created for autogroups don't have cgroups associated with them and hence no dentries and hence no pathnames.

Mike didn't remove it, but _was_ supposed to get around to it.

Quoted 1 line

I guess we could do fix this as shown in the attached patch.

+#ifdef CONFIG_CGROUP_SCHED
+ {
+ char path[64];
+

...

+#if defined(CONFIG_CGROUP_SCHED) && defined(CONFIG_FAIR_GROUP_SCHED)
+ char path[128];

One reason it was removed was path[64/128].

-Mike

#210 lore raw

Re: [tip:sched/core] sched: Add 'autogroup' scheduling feature: automated per session task groups

Quoted 36 lines · Mike Galbraith
Quoted 10 lines · Bharata B Rao
Quoted 6 lines · Peter Zijlstra
Quoted 2 lines · Bharata B Rao

The autogroup patchset removes the display of cgroup name from sched_debug output.

Hrmph.. that wasn't supposed to happen, care to send a patch to fix that up?

There are two aspects here:

Quoted 8 lines

- Printing cgroup name for per-CPU cfs_rqs shouldn't be affected by autogroup and the old code should work here.
- Printing cgroup name for tasks depends on task_group(), which has been changed by autogroup patch. I haven't really looked deep into autogroup patch, but from whatever I can gather, Mike had a reason to remove this bit from sched_debug. The task groups created for autogroups don't have cgroups associated with them and hence no dentries and hence no pathnames.

Mike didn't remove it, but _was_ supposed to get around to it.

Quoted 1 line

I guess we could do fix this as shown in the attached patch.

kernel/sched_debug.c +6 -0
+#ifdef CONFIG_CGROUP_SCHED
+       {
+               char path[64];
+
 
...
 
+#if defined(CONFIG_CGROUP_SCHED) && defined(CONFIG_FAIR_GROUP_SCHED)
+       char path[128];
Quoted 1 line

One reason it was removed was path[64/128].

Other callers of cgroup_path use PATH_MAX=4096 here. I believe the original reason for these short path sizes was to be light on stack and as well as to avoid allocation. Can we have some reasonable length (256 or 512 ?) and live with truncation (if that ever happens) ?

Also, while displaying group name with tasks, does it make sense to display autogroup-<id> (the one shown in /proc/<pid>/autogroup) ?

Regards,
Bharata.

#211 lore raw

Re: [tip:sched/core] sched: Add 'autogroup' scheduling feature: automated per session task groups

Quoted 37 lines · Bharata B Rao
Quoted 36 lines · Mike Galbraith
Quoted 10 lines · Bharata B Rao
Quoted 6 lines · Peter Zijlstra
Quoted 2 lines · Bharata B Rao

The autogroup patchset removes the display of cgroup name from sched_debug output.

Hrmph.. that wasn't supposed to happen, care to send a patch to fix that up?

There are two aspects here:

Quoted 8 lines

- Printing cgroup name for per-CPU cfs_rqs shouldn't be affected by autogroup and the old code should work here.
- Printing cgroup name for tasks depends on task_group(), which has been changed by autogroup patch. I haven't really looked deep into autogroup patch, but from whatever I can gather, Mike had a reason to remove this bit from sched_debug. The task groups created for autogroups don't have cgroups associated with them and hence no dentries and hence no pathnames.

Mike didn't remove it, but _was_ supposed to get around to it.

Quoted 1 line

I guess we could do fix this as shown in the attached patch.

kernel/sched_debug.c +6 -0
+#ifdef CONFIG_CGROUP_SCHED
+       {
+               char path[64];
+
 
...
 
+#if defined(CONFIG_CGROUP_SCHED) && defined(CONFIG_FAIR_GROUP_SCHED)
+       char path[128];
Quoted 6 lines
Quoted 1 line

One reason it was removed was path[64/128].

Other callers of cgroup_path use PATH_MAX=4096 here. I believe the original reason for these short path sizes was to be light on stack and as well as to avoid allocation. Can we have some reasonable length (256 or 512 ?) and live with truncation (if that ever happens) ?

I don't see why not, unlikely situation in non-critical path.

Quoted 2 lines

Also, while displaying group name with tasks, does it make sense to display autogroup-<id> (the one shown in /proc/<pid>/autogroup) ?

I did to me obviously :) I'm fine with it not showing up, though if it survives and evolves, maybe it'll want visibility. ATM, you know what's in what group via ps -o foo,session.

-Mike

#212 lore raw

Re: [PATCH v4] sched: automated per session task groups

In reply to Mike Galbraith
Quoted 4 lines · Mike Galbraith

A recurring complaint from CFS users is that parallel kbuild has a negative impact on desktop interactivity.  This patch implements an idea from Linus, to automatically create task groups.  This patch only per session autogroups, but leaves the way open for enhancement.

Resurrecting this thread a bit, one question I didn't see discussed is simply:

Why doesn't "nice" work for this? On my Fedora 14 system, "ps alxf" shows almost everything in my session is running at the default nice
0. The only exceptions are "/usr/libexec/tracker-miner-fs" at 19, and pulseaudio at -11.

I don't know What would happen if say the scheduler effectively group-scheduled each nice value? Then, what we tell people to do is run "nice make". Which in fact, has been documented as a thing to do for decades. Actually I tend to use "ionice" too, which is also useful if any of your desktop applications happen to make the mistake of doing I/O in the mainloop (emacs fsync()ing in UI thread, I'm looking at you).

Quickly testing kernel-2.6.35.6-48.fc14.x86_64 on a "Intel(R)
Core(TM)2 Quad CPU Q9400 @ 2.66GHz", the difference between "make
-j 128" and "nice make -j 128" is quite noticeable. As you'd expect. The CFS docs already say:

"The CFS scheduler has a much stronger handling of nice levels and SCHED_BATCH than the previous vanilla scheduler: both types of workloads are isolated much more aggressively"

Does it just need to be even more aggressive, and people use "nice"?

#213 lore raw

Re: [PATCH v4] sched: automated per session task groups

Quoted 5 lines · Colin Walters

Why doesn't "nice" work for this?  On my Fedora 14 system, "ps alxf" shows almost everything in my session is running at the default nice
0.  The only exceptions are "/usr/libexec/tracker-miner-fs" at 19, and pulseaudio at -11.

"nice" doesn't work. It never has. Nobody ever uses it, and that has always been true.

As you note, you can find occasional cases of it being used, but they are either for things that are _so_ unimportant (and know they are) and annoying cpu hogs that they wouldn't be allowed to live unless they were niced down maximally (your tracker-miner example), or they use nice not because they really want to, but because it is an approximation for what they really do want (ie pulseaudio wants low latencies, and is set up by the distro, so you'll find it niced up).

But the fundamental issue is that 'nice' is broken. It's very much broken at a conceptual and technical design angle (absolute priority levels, no fairness), but it's broken also from a psychological and practical angle (ie expecting people to manually do extra work is ridiculous and totally unrealistic).

Quoted 2 lines

I don't know What would happen if say the scheduler effectively group-scheduled each nice value?

Why would you want to do that? If you are willing to do group scheduling, do it on something sane and meaningful, and something that doesn't need user interaction or decisions. And do it on something that has more than 20 levels.

You could, for example, decide to do it per session.

Quoted 3 lines

 Then, what we tell people to do is
run "nice make".  Which in fact, has been documented as a thing to do for decades.

Nobody but morons ever "documented" that. Sure, you can find people saying it, but you won't be finding people actually _doing_ it. Look around.

Seriously. Nobody _ever_ does "nice make", unless they are seriously repressed beta-males (eg MIS people who get shouted at when they do system maintenance unless they hide in dark corners and don't get discovered). It just doesn't happen.

But more fundamentally, it's still the wrong thing to do. What nice level should you use?

And btw, it's not just "make". One of the things that originally caused me to want something like this is that you can enable some pretty aggressive threading with "git diff". If you use the "core.preloadindex" setting, git will fire up 20 threads just to do "lstat()" system calls as quickly as it humanly can. Or "git grep" will happily use lots of threads and really mess with your system, except it limits the threads to a smallish number just to not be asocial.

Do you want to do "nice git" too? Especially as the reason the threaded lstat was implemented was that over NFS, you actually want the threads not because you're using lots of CPU, but because you want to fire up lots of concurrent network traffic - and you actually want low latency. So you do NOT want to mark these threads as "unimportant". They're not.

But what you do want is a basic and automatic fairness. When I do "git grep", I want the full resources of the machine to do the grep for me, so that I can get the answer in half a second (which is about the limit at which point I start getting impatient). That's an _important_ job for me. It should get all the resources it can, there is absolutely no excuse for nicing it down.

But at the same time, if I just happen to have sound or something going on at the same time, I would definitely like some amount of fairness. Just because git is smart and can use lots of threads to do its work quickly, it shouldn't be _unfair_. It should hod the machine
- but only up to a point of some fairness.

That is something that "nice" can never give you. It's not what nice was designed for, it's not how nice works. And if you ask people to say "this work isn't important", you shouldn't expect them to actually do it. If something isn't important, I certainly won't then spend extra effort on it, for chrissake!

Now, I'm not saying that cgroups are necessarily the answer either. But using sessions as input to group scheduling is certainly _one_ answer. And it's a hell of a better answer than 'nice' has ever been, or will ever be.

Linus

#214 lore raw

Re: [PATCH v4] sched: automated per session task groups

Quoted 6 lines · Linus Torvalds

But the fundamental issue is that 'nice' is broken. It's very much broken at a conceptual and technical design angle (absolute priority levels, no fairness), but it's broken also from a psychological and practical angle (ie expecting people to manually do extra work is ridiculous and totally unrealistic).

I don't see it as ridiculous - for the simple reason that it really has existed for so long and is documented (see below).

Quoted 4 lines

Why would you want to do that? If you are willing to do group scheduling, do it on something sane and meaningful, and something that doesn't need user interaction or decisions. And do it on something that has more than 20 levels.

In this case, the "user interaction" component is pretty damn small. We're talking about 4 extra characters.

Quoted 3 lines

Nobody but morons ever "documented" that. Sure, you can find people saying it, but you won't be finding people actually _doing_ it. Look around.

Look around...where? On what basis are you making that claim? I did a quick web search for "unix background process", and this tutorial (in the first page of Google search results) aimed at grad students who use Unix at college definitely describes "nice make":
http://acs.ucsd.edu/info/jobctrl.shtml

There are some that don't, like:
http://linux.about.com/od/itl_guide/a/gdeitl35t01.htm and
http://www.albany.edu/its/quickstarts/qs-common_unix.html

But then again here's a Berkeley "Unix Tutorial" that does cover it:
http://people.ischool.berkeley.edu/~kevin/unix-tutorial/section13.html

So, does your random Linux-using college student or professional developer know about "nice"? My guess is "likely". Do they use it for "make"? No data. The issue is that you really only have a bad experience on *large* projects. But if we just said to people who come to us "Hey, when I compile webkit/linux/mozilla my system slows down" we can tell them "use nice", especially since it's already documented on the web, that seems to me like a pretty damn good answer.

Quoted 4 lines

Seriously. Nobody _ever_ does "nice make", unless they are seriously repressed beta-males (eg MIS people who get shouted at when they do system maintenance unless they hide in dark corners and don't get discovered). It just doesn't happen.

Heh. Well, I do at least (or rather, my personal automagic build wrapper script does (it detects Makefile/autotools etc. and tries to DTRT)).

Quoted 2 lines

But more fundamentally, it's still the wrong thing to do. What nice level should you use?

Doesn't matter - if they all got group-scheduled together, then the default of 10 (0+10) is totally fine.

Quoted 6 lines

Do you want to do "nice git" too? Especially as the reason the threaded lstat was implemented was that over NFS, you actually want the threads not because you're using lots of CPU, but because you want to fire up lots of concurrent network traffic - and you actually want low latency. So you do NOT want to mark these threads as "unimportant". They're not.

Hmm...how many threads are we talking about here? If it's just say one per core, then I doubt it needs nicing. The reason people nice make is because the whole thing alternates between being CPU bound and I/O bound, so you need to start more jobs than cores (sometimes a lot more) to ensure maximal utilization.

Quoted 6 lines

But what you do want is a basic and automatic fairness. When I do "git grep", I want the full resources of the machine to do the grep for me, so that I can get the answer in half a second (which is about the limit at which point I start getting impatient). That's an _important_ job for me. It should get all the resources it can, there is absolutely no excuse for nicing it down.

Sure...though I imagine for "most" people that's totally I/O bound (either on ext4 journal or hard disk seeks).

Quoted 4 lines

Now, I'm not saying that cgroups are necessarily the answer either. But using sessions as input to group scheduling is certainly _one_ answer. And it's a hell of a better answer than 'nice' has ever been, or will ever be.

Well, the text of Documentation/scheduler/sched-design-CFS.txt certainly seems to be claiming it was a big improvement in this kind of situation from the previous scheduler. If we're finding out there are cases where it's not, it's definitely worth asking the question why it's not working.

Speaking of the scheduler documentation - note that its sample shell code contains exactly the problem showing what's wrong with auto-grouping-by-tty, which is:

# firefox &	# Launch firefox and move it to "browser" group

As soon as you do that from the same terminal that you're going to launch the "make" from, you're back to total lossage. Are you going to explain to a student that "oh, you need to create a new gnome-terminal tab and launch firefox from that"?

#215 lore raw

Re: [PATCH v4] sched: automated per session task groups

Quoted 3 lines · Colin Walters

But then again here's a Berkeley "Unix Tutorial" that does cover it:
http://people.ischool.berkeley.edu/~kevin/unix-tutorial/section13.html

What part of "nobody does that" didn't you understand?

I know about "nice". I think it was part of the original Unix course I ever took, and it probably made more sense back then (sixteen people at a time on a microvax, compiling stuff). And I never used it, afaik. Nor does really anybody else.

But hey, whatever floats your boat. You can use it. And you can feel special and better than the rest of us exactly because you know you really _are_ special.

Quoted 2 lines

Hmm...how many threads are we talking about here?  If it's just say one per core, then I doubt it needs nicing.

I think git defaults to a maximum of 20 for it. Remember: it's not about "cores". It's about IO, and then 20 is a "let's not mess up everybody else _too_ much when we're actually CPU-bound".

But that's not the point. The point is that "nice" is totally the wrong thing to do. It's _always_ the wrong thing to do. The only reason it's in tutorials and taught in intro Unix classes is that it's the only thing there is in traditional unix.

And we can be better. We don't need to be stupid and traditional.

But you go right on and use it. Nobody stops you.

Quoted 2 lines

Sure...though I imagine for "most" people that's totally I/O bound (either on ext4 journal or hard disk seeks).

Sure. And "most" people do something totally different. What's your point? The fact is, the session-based group scheduling really does work. It works on a lot of different loads. It's nice for things like my use, but it's _also_ nice for things like me ssh'ing into my kids or wife's computers to update their kernel. And it's nice for things like "make -j test" for git etc.

And it doesn't hurt you. If you're happy with "nice", go on and use it. Why are you even discussing it? I'm telling you the FACT that others aren't happy with nice, and that smart people consider nice to be totally useless.

But none of that means that you can't go on using it. Comprende?

Quoted 4 lines
# firefox &     # Launch firefox and move it to "browser" group

As soon as you do that from the same terminal that you're going to launch the "make" from, you're back to total lossage.

"Mommy mommy, it hurts when I stick forks in my eyes!"

What's your point again? It's a heuristic. It works great for the cases many normal people have. If you have a graphical desktop, most sane people would tend to start the browser from that nice big browser icon. But again, if you want to stick forks in your eyes, go right ahead. It's not _my_ problem.

And similarly, it's not _your_ problem if other people want to do saner things, is it?

Linus

#216 lore raw

Re: [PATCH v4] sched: automated per session task groups

Quoted 2 lines · Linus Torvalds

And it doesn't hurt you. If you're happy with "nice", go on and use it. Why are you even discussing it?

Because it seems to me like a bug if it isn't as good as group scheduling? Most of your message is saying it's worthless, and I don't disagree that it's not very good *right now*. I guess where we disagree is whether it's worth fixing.

Quoted 1 line

What's your point again? It's a heuristic.

So if it's a heuristic the OS can get wrong, wouldn't it be a good idea to support a way for programs and/or interactive users to explicitly specify things? Unfortunately the cgroups utilities don't make this easy (and of course there's the issue that no major released OS exports write permission to the cpu cgroup for a desktop session uid). I guess "nice" could be patched to, if the user has permission to the cgroups, to auto-create a group. Or...nice could be fixed.

On a more productive note, I see now Documentation/scheduler/sched-nice-design.txt has a lot of really useful history regarding "nice" and the complaints over time (I guess this is where some of your assertions that it's failed/worthless comes from).

#217 lore raw

Re: [PATCH v4] sched: automated per session task groups

Quoted 10 lines · Colin Walters
Quoted 2 lines · Linus Torvalds

And it doesn't hurt you. If you're happy with "nice", go on and use it. Why are you even discussing it?

Because it seems to me like a bug if it isn't as good as group scheduling?  Most of your message is saying it's worthless, and I don't disagree that it's not very good *right now*.  I guess where we disagree is whether it's worth fixing.

It's not worth 'fixing", because it works exactly like it's designed - and supposed - to work.

There really isn't anything to fix. 'nice' is what it is. It's a simple legacy interface to scheduler priority. The fact that it's also almost totally useless is irrelevant. It's like male nipples. We wouldn't be better off lactating, and they look like some odd wart that doesn't do much good. But it would be worse to remove it.

'nice' is a bad idea. It's a bad idea that has perfectly understandable historical reasons for it, but it's an _unfixably_ bad idea.

Linus

#218 lore raw

Re: [PATCH v4] sched: automated per session task groups

In reply to Colin Walters
Quoted 3 lines · Colin Walters

So if it's a heuristic the OS can get wrong, wouldn't it be a good idea to support a way for programs and/or interactive users to explicitly specify things?

Consider a multi-user machine. nice is an orthogonal concern in that case. Therefore, fixing nice doesn't address all issues. Also: Most linux systems are multi-user (root and the physical tty user.) Further, even a single user wears multiple hats on a single system. The idea is to infer those hats, and deal with them fairly.

No one is taking nice away from you. Keep using it if you like.

If you want to allow users to explicitly specify group scheduling, then good news: we already have that feature. You just seem to not be using it. Much like the other 99.993% of us.

The kernel is supposed to have *sane defaults*. That's what is under discussion here.

~r.

#219 lore raw

Re: [PATCH v4] sched: automated per session task groups

Quoted 7 lines · Ray Lee
Quoted 3 lines · Colin Walters

So if it's a heuristic the OS can get wrong, wouldn't it be a good idea to support a way for programs and/or interactive users to explicitly specify things?

Consider a multi-user machine. nice is an orthogonal concern in that case. Therefore, fixing nice doesn't address all issues.

For the purposes of this discussion again, let's say "fixing nice" means say "group schedule each nice level above 0". There are obviously many possibilities here, but let's consider this one precisely.

How, exactly, under what scenario in a "multi-user machine" does this break? How exactly is it orthogonal?

Two people logged in would get their "make" jobs group scheduled together. What is the problem?

Since Linus appears to be more interested in talking about nipples than explaining exactly what it would break, but you appear to agree with him, hopefully you'll be able to explain...

#220 lore raw

Re: [PATCH v4] sched: automated per session task groups

Quoted 5 lines · Colin Walters

For the purposes of this discussion again, let's say "fixing nice" means say "group schedule each nice level above 0".  There are obviously many possibilities here, but let's consider this one precisely.

THAT IS NOT HOW 'nice' WORKS!

For chissake, how hard is it to understand?

The semantics of "nice" are not - and have never been - to put things into process scheduling groups of their own.

When somebody says "nice xyzzy", they are explicitly stating that "xyzzy" isn't as important as other processes. It's done for stuff that you don't care about, and more specifically, for stuff that you really don't want to impact anything else. So if there are other things to be run, 'nice' means that those should get more CPU time.

(Obviously, negative nice levels work the other way around).

This is very much documented. People rely on it. Look at the man-page. It talks about "most favorable" vs "least favorable" scheduling.

Quoted 2 lines

Two people logged in would get their "make" jobs group scheduled together.  What is the problem?

The problem is that you don't know what the hell you are talking about.

Different nice levels shouldn't get group scheduled together - they should be scheduled *less*. And it's not about "make", since nobody really ever uses nice on make anyway, it's about things like pulseaudio (that wants higher priorities) and random background filesystem indexers etc (that want lower priorities).

Nice levels are _not_ about group scheduling. They're about priorities. And since the cgroup code doesn't even support priority levels for the groups, it's a really *horrible* match.

And the thing is, the nice semantics are traditional. They are also *horrible*, but that doesn't allow you to change their semantics. People rely on those crazy traditional and mostly useless semantics. Not very much (because they are mostly useless), but there really are people who use it.

And they use it knowing that positive nice levels means that something is less important.

In contrast, giving processes a scheduling group doesn't imply "less important". Not AT ALL. It doesn't really mean "more important" either, it just means "somewhat insulated from other groups".

So let's say that you have a filesystem indexer, and you nice it up to make sure that it doesn't steal CPU bandwidth from your "real work". Now, let's say that you start a "make -16" to build something important.

Do you *really* think that the person who niced the filesystem indexer down wants the indexer to get 50% of the CPU, just because it's scheduled separately from the parallel make?

HELL NO!

So stop this idiocy. "nice" has absolutely nothing to do with group scheduling. It cannot. It must not. It's a legacy interface, and it has real semantics.

Quoted 3 lines

Since Linus appears to be more interested in talking about nipples than explaining exactly what it would break, but you appear to agree with him, hopefully you'll be able to explain...

The reason I was talking about make nipples should be clear by now. Think "legacy interface". Think "don't mess with it, because people are used to it".

They may be useless, but dammit, they do what they do.

Don't try to turn male nipples into something they aren't. And don't try to turn 'nice' into something it isn't.

Linus

#221 lore raw

Re: [PATCH v4] sched: automated per session task groups

Quoted 3 lines · Linus Torvalds

The semantics of "nice" are not - and have never been - to put things into process scheduling groups of their own.

Again, I obviously understand that - the point is to explore the space of changes here and consider what would (and wouldn't) break. And actually, what would improve.

Quoted 1 line

This is very much documented. People rely on it.

Well, we established my Fedora 14 system doesn't. You said "no one" uses "nice" interactively. So...that leaves - who? If you were saying to me something like "I know Yahoo has some code in their data centers which uses a range of nice values; if we made this change, all of a sudden they'd get more CPU contention..." Or like, "I'm pretty sure Maemo uses very low nice values for some UI code". But you so far haven't really done that, it's just been (mostly) assertions/handwaving. Now you obviously have a lot more experience that gives those assertions and handwaving a lot of credibility - but all we need is one concrete example to shut me up =)

Playing around with Google code search a bit, hits for "nice" were almost all duplicates of various C library headers/implementations. "setpriority" was a bit more interesting, it appears Chromium has some code to bump up the nice value by 5 for "background" processes:

http://google.com/codesearch/p?hl=en#OAMlx_jo-ck/src/base/process_linux.cc&q=setpriority&exact_package=chromium&l=21

But all my Chrome related processes here are 0, so who knows what
that's used for. There are also hits for chromium's copy of embedded
cygwin+perl...terrifying. I assume (hope, desperately) that Cygwin+Perl is just used for building...

Another hit here in some random X screensaver code:
http://google.com/codesearch/p?hl=en#tJJawb1IJ20/driver/exec.c&q=setpriority%20file:.*.c&l=218

But I can't find a place where it's setting a non-zero value for that.

So...ah, here's one in Android's "development" git:
http://google.com/codesearch/p?hl=en#CRBM04-7BoA/simulator/wrapsim/Init.c&q=setpriority%20file:.*.c&l=91

Except it appears to be unused =/

Oh! Here we go, one in the Android UI code:
http://google.com/codesearch/p?hl=en#uX1GffpyOZk/libs/rs/rsContext.cpp&q=setpriority%20file:.*.c&sa=N&cd=29&ct=rc
Pasting this one so people don't have to follow the link:

void * Context::threadProc(void *vrsc)
{
...
setpriority(PRIO_PROCESS, rsc->mNativeThreadId, ANDROID_PRIORITY_DISPLAY);

}

Where ANDROID_PRIORITY_DISPLAY = -4. Actually the whole enum is interesting:
http://google.com/codesearch/p?hl=en#uX1GffpyOZk/include/utils/threads.h&q=ANDROID_PRIORITY_DISPLAY&l=39

One interesting bit here is that they renice UI that the user is presently interacting with:

/* threads currently running a UI that the user is interacting with */
ANDROID_PRIORITY_FOREGROUND = -2,

(Something "we" (and by "we" I mean GNOME) don't do, I believe Windows does though). Though, honestly I could whip up a gnome-settings-daemon plugin to do this in about 10 minutes. Maybe after dinner.

So...we've established that important released operating systems do use negative nice values (not surprising). I can't offhand find any uses of e.g. ANDROID_PRIORITY_BACKGROUND (i.e. a positive nice value) in the "base" sources though.

Quoted 2 lines

Different nice levels shouldn't get group scheduled together - they should be scheduled *less*.

But it seems obvious (right?) that putting them in one group *will* ensure they get scheduled less, since that one group has to contend with all other processes.

Quoted 3 lines

And it's not about "make", since nobody
really ever uses nice on make anyway, it's about things like pulseaudio (that wants higher priorities)

Note that pulse is actually using the RT scheduling class, so (I think) its actual nice value is irrelevant.

Again using F14, the only things using negative nice besides pulse is udev and auditd.

Quoted 2 lines

Not very much (because they are mostly useless), but there really are people who use it.

Still trying to extract specific examples of "people who use it" from you...

Quoted 3 lines

Do you *really* think that the person who niced the filesystem indexer down wants the indexer to get 50% of the CPU, just because it's scheduled separately from the parallel make?

Finally, an example! I can work with this. So let's assume I'm using some JavaScript-intensive website in Firefox in GNOME, and tracker-miner-fs kicks in after noticing I just saved a Word document I want to look at later. And an otherwise idle system. You're suggesting that, now tracker-miner-fs would be using a lot more CPU if it was in an empty group than it would have before?

That does seem likely to be true. But would it be a *problem*? I don't know, it's not obvious to me offhand. Especially on any hardware that's dual-core, where SpiderMonkey can be burning one core (since that's all it will use, modulo Web Workers), and tracker on another.

Anyways, I don't have the kernel-fu to make a patch myself here, especially since the scheduler is probably one of the hardest parts of the OS. So ultimately I guess, if you just totally disagree, fine. But I wasn't satisfied with the response - my engineering intuition is to work through problems and try to really understand what would be wrong. It's hard to accept "just trust me, that's stupid".

#222 lore raw

Re: [PATCH v4] sched: automated per session task groups

Quoted 12 lines · Colin Walters
Quoted 3 lines · Linus Torvalds

The semantics of "nice" are not - and have never been - to put things into process scheduling groups of their own.

Again, I obviously understand that - the point is to explore the space of changes here and consider what would (and wouldn't) break. And actually, what would improve.

Quoted 1 line

This is very much documented. People rely on it.

Quoted 11 lines

Well, we established my Fedora 14 system doesn't. You said "no one" uses "nice" interactively. So...that leaves - who? If you were saying to me something like "I know Yahoo has some code in their data centers which uses a range of nice values; if we made this change, all of a sudden they'd get more CPU contention..." Or like, "I'm pretty sure Maemo uses very low nice values for some UI code". But you so far haven't really done that, it's just been (mostly) assertions/handwaving. Now you obviously have a lot more experience that gives those assertions and handwaving a lot of credibility - but all we need is one concrete example to shut me up =)

[...]

I'll give you two re-world examples from two (closed source, but still) apps we develop at my current employer.

The first one is a server/network monitoring app where there are lots of child processes devoted to performing checks, storing data, displaying results etc. Most of these processes just run at the default nice level. One of the processes sometimes has a need for a cryptographic key pair and it can generate this when it needs it, but it's better if one is reaily available, so we have a seperate child process running that maintains a small pool of new key pairs - this process runs at a high nice level since it should not take CPU time away from the rest of the processes (it's not important, it's just a small optimization), the need for key pairs comes at large intervals, so the pool will almost never be depleted even if this process doesn't get very much CPU time for a long time and besides, if the pool ever gets depleted its no disaster since the consumer will then just generate a key pair when needed and burn the required CPU.

The second is a backup aplication where one child process is in charge of doing background disk scanning, compression and encryption. This process is not interactive, it must result in minimal interference with whatever the user is currently using the machine for as his primary task and time-to-completion is not really that important. So, this process runs at a rather high nice level to avoid stealing CPU from the users primary task(s).

Signature · 3 lines
Jesper Juhl <[email protected]>            http://www.chaosbits.net/
Don't top-post http://www.catb.org/~esr/jargon/html/T/top-post.html
Plain text mails only, please.