Skip to contentSkip to conversation

sched: SCHED_DEADLINE v5

RFC PATCH In review Juri Lelli 3 participants 26 messages May 23, 2012 – May 30, 2012

Subsystems (25)
lore
Share & tools
lore

Juri Lelli

author
#0 lore raw

cover sched: SCHED_DEADLINE v5

Hello everyone,

This is the take 5 for the SCHED_DEADLINE patchset.

The patchset introduces a new deadline based real-time task scheduling policy --called SCHED_DEADLINE-- with bandwidth isolation (aka "resource reservation") capabilities. It supports global/clustered multiprocessor scheduling through dynamic task migrations.

From the previous releases[1]:
- comments and fixes coming from the reviews we got have been considered and mostly applied; still awaiting:

* Steven Rostedt's "take into account the RT tasks" when migrating DL tasks; the DL push/pull mechanism will probably see major changes (RT tasks accounting included) on which we are currently working;
* Peter Zijlstra's "gcc work-around"; based on a not yet mainline accepted patch;
* Peter Zijlstra's "unify the use of nr_cpus_allowed" suggestion was addressed too; however this changes too are based on a not yet merged patch.

- this release is on top of tip/master (as of today), so the actual news is the rebase on v3.4 :-); anyway, -rt guys can find -rt branches on the repo (more info below);

- first two patches are Peter Zijlstra's math128; later patches are a use-case for them.

Still missing/incomplete:
- (c)group based bandwidth management, and maybe scheduling;
- bandwidth inheritance (to replace deadline/priority inheritance);
- access control for non-root users (and related security concerns to address).

The development is taking place at:
https://github.com/jlelli/sched-deadline

The repo has been cleaned-up, main branches are now:

- mainline-dl: tracking tip/master (raw commits);
- linux-rt-dl: tracking -rt releases;
- linux-stable-rt-dl: tracking Rostedt's 3.2-stable-rt branch[2];
- sched-dl-V5: this patchset on top of tip/master.

Check the repositories frequently if you're interested, and feel free to e-mail me for any issue you run into.

Test application:
https://github.com/gbagnoli/rt-app

Development mailing list: linux-dl; you can subscribe from here:
http://feanor.sssup.it/mailman/listinfo/linux-dl
or via e-mail (send a message to [email protected] with just the word `help' as subject or in the body to receive info).

There is also a parallel branch maintained by Insop Song (from Ericsson)
(https://github.com/insop/sched-deadline2). Ericsson is in fact evaluating the use of SCHED_DEADLINE for CPE (Customer Premise Equipment) devices in order to reserve CPU bandwidth to processes.

The code was being jointly developed by ReTiS Lab (http://retis.sssup.it)
and Evidence S.r.l (http://www.evidence.eu.com) in the context of the ACTORS EU-funded project (http://www.actors-project.eu). It is now also supported by the S(o)OS EU-funded project (http://www.soos-project.eu/).
It has also some users, both in academic and applied research. We got positive feedbacks from Ericsson (see above), Wind River, Porto (ISEP), Trento, Lund and Malardalen universities.

As usual, any kind of feedback is welcome and appreciated.

Thanks in advice and regards,

- Juri

[1] http://lwn.net/Articles/376502, http://lwn.net/Articles/353797,

    http://lwn.net/Articles/412410, http://lwn.net/Articles/490944

[2] http://www.kernel.org/pub/linux/kernel/projects/rt/3.2/README

Dario Faggioli (9):
sched: add sched_class->task_dead.
sched: add extended scheduling interface.
sched: SCHED_DEADLINE structures & implementation.
sched: SCHED_DEADLINE avg_update accounting.
sched: add schedstats for -deadline tasks.
sched: add latency tracing for -deadline tasks.
sched: drafted deadline inheritance logic.
sched: add bandwidth management for sched_dl.
sched: add sched_dl documentation.

Peter Zijlstra (3):
math128: Introduce various 128bit primitives math128, x86_64: Implement {mul,add}_u128 in 64bit asm
rtmutex: turn the plist into an rb-tree.

Juri Lelli (2):
sched: SCHED_DEADLINE SMP-related data structures & logic.
sched: speed up -dl pushes with a push-heap.

Harald Gustafsson (1):
sched: add period support for -deadline tasks.

 Documentation/scheduler/sched-deadline.txt |  164 +++
 arch/alpha/include/asm/Kbuild              |    1 +
 arch/arm/include/asm/Kbuild                |    1 +
 arch/arm/include/asm/unistd.h              |    3 +
 arch/arm/kernel/calls.S                    |    3 +
 arch/avr32/include/asm/Kbuild              |    1 +
 arch/blackfin/include/asm/Kbuild           |    1 +
 arch/c6x/include/asm/Kbuild                |    1 +
 arch/cris/include/asm/Kbuild               |    1 +
 arch/frv/include/asm/Kbuild                |    1 +
 arch/h8300/include/asm/Kbuild              |    1 +
 arch/hexagon/include/asm/Kbuild            |    1 +
 arch/ia64/include/asm/Kbuild               |    1 +
 arch/m32r/include/asm/Kbuild               |    1 +
 arch/m68k/include/asm/Kbuild               |    1 +
 arch/microblaze/include/asm/Kbuild         |    1 +
 arch/mips/include/asm/Kbuild               |    1 +
 arch/mn10300/include/asm/Kbuild            |    1 +
 arch/openrisc/include/asm/Kbuild           |    1 +
 arch/parisc/include/asm/Kbuild             |    1 +
 arch/powerpc/include/asm/Kbuild            |    1 +
 arch/s390/include/asm/Kbuild               |    1 +
 arch/score/include/asm/Kbuild              |    1 +
 arch/sh/include/asm/Kbuild                 |    1 +
 arch/sparc/include/asm/Kbuild              |    1 +
 arch/tile/include/asm/Kbuild               |    1 +
 arch/um/include/asm/Kbuild                 |    1 +
 arch/unicore32/include/asm/Kbuild          |    1 +
 arch/x86/include/asm/math128.h             |   39 +
 arch/x86/syscalls/syscall_32.tbl           |    3 +
 arch/x86/syscalls/syscall_64.tbl           |    3 +
 arch/xtensa/include/asm/Kbuild             |    1 +
 include/asm-generic/math128.h              |    4 +
 include/linux/init_task.h                  |   10 +
 include/linux/math128.h                    |  180 +++
 include/linux/rtmutex.h                    |   18 +-
 include/linux/sched.h                      |  155 ++-
 include/linux/syscalls.h                   |    7 +
 kernel/fork.c                              |    8 +-
 kernel/hrtimer.c                           |    2 +-
 kernel/rtmutex-debug.c                     |   10 +-
 kernel/rtmutex.c                           |  161 ++-
 kernel/rtmutex_common.h                    |   22 +-
 kernel/sched/Makefile                      |    4 +-
 kernel/sched/core.c                        |  689 +++++++++++-
 kernel/sched/cpudl.c                       |  208 ++++
 kernel/sched/cpudl.h                       |   33 +
 kernel/sched/debug.c                       |   46 +
 kernel/sched/dl.c                          | 1646 ++++++++++++++++++++++++++++
 kernel/sched/rt.c                          |    2 +-
 kernel/sched/sched.h                       |  143 +++
 kernel/sched/stop_task.c                   |    2 +-
 kernel/sysctl.c                            |   14 +
 kernel/trace/trace_sched_wakeup.c          |   44 +-
 kernel/trace/trace_selftest.c              |   30 +-
 lib/Makefile                               |    2 +-
 lib/math128.c                              |   40 +
 57 files changed, 3586 insertions(+), 135 deletions(-)
author
#1 lore raw

PATCH 1/15 math128: Introduce various 128bit primitives

From: Peter Zijlstra <[email protected]>

Grow rudimentary u128 support without relying on gcc/libgcc.

---
 arch/alpha/include/asm/Kbuild      |    1 +
 arch/arm/include/asm/Kbuild        |    1 +
 arch/avr32/include/asm/Kbuild      |    1 +
 arch/blackfin/include/asm/Kbuild   |    1 +
 arch/c6x/include/asm/Kbuild        |    1 +
 arch/cris/include/asm/Kbuild       |    1 +
 arch/frv/include/asm/Kbuild        |    1 +
 arch/h8300/include/asm/Kbuild      |    1 +
 arch/hexagon/include/asm/Kbuild    |    1 +
 arch/ia64/include/asm/Kbuild       |    1 +
 arch/m32r/include/asm/Kbuild       |    1 +
 arch/m68k/include/asm/Kbuild       |    1 +
 arch/microblaze/include/asm/Kbuild |    1 +
 arch/mips/include/asm/Kbuild       |    1 +
 arch/mn10300/include/asm/Kbuild    |    1 +
 arch/openrisc/include/asm/Kbuild   |    1 +
 arch/parisc/include/asm/Kbuild     |    1 +
 arch/powerpc/include/asm/Kbuild    |    1 +
 arch/s390/include/asm/Kbuild       |    1 +
 arch/score/include/asm/Kbuild      |    1 +
 arch/sh/include/asm/Kbuild         |    1 +
 arch/sparc/include/asm/Kbuild      |    1 +
 arch/tile/include/asm/Kbuild       |    1 +
 arch/um/include/asm/Kbuild         |    1 +
 arch/unicore32/include/asm/Kbuild  |    1 +
 arch/x86/include/asm/Kbuild        |    1 +
 arch/xtensa/include/asm/Kbuild     |    1 +
 include/asm-generic/math128.h      |    4 +
 include/linux/math128.h            |  180 ++++++++++++++++++++++++++++++++++++
 lib/Makefile                       |    2 +-
 lib/math128.c                      |   40 ++++++++
 31 files changed, 252 insertions(+), 1 deletion(-)
 create mode 100644 include/asm-generic/math128.h
 create mode 100644 include/linux/math128.h
 create mode 100644 lib/math128.c
arch/alpha/include/asm/Kbuild +1 -0
index e423def..f8f9bed 100644
@@ -8,3 +8,4 @@ header-y += pal.h
 header-y += reg.h
 header-y += regdef.h
 header-y += sysinfo.h
+generic-y += math128.h
arch/arm/include/asm/Kbuild +1 -0
index 960abce..a55a259 100644
@@ -18,3 +18,4 @@ generic-y += resource.h
 generic-y += sections.h
 generic-y += siginfo.h
 generic-y += sizes.h
+generic-y += math128.h
arch/avr32/include/asm/Kbuild +1 -0
index 3136628..6c8e565 100644
@@ -1,3 +1,4 @@
 include include/asm-generic/Kbuild.asm
 
 header-y	+= cachectl.h
+generic-y += math128.h
arch/blackfin/include/asm/Kbuild +1 -0
index 5a0625a..6836e68 100644
@@ -47,3 +47,4 @@ generic-y += xor.h
 header-y += bfin_sport.h
 header-y += cachectl.h
 header-y += fixed_code.h
+generic-y += math128.h
arch/c6x/include/asm/Kbuild +1 -0
index 3af601e..0f84f29 100644
@@ -51,3 +51,4 @@ generic-y += types.h
 generic-y += ucontext.h
 generic-y += user.h
 generic-y += vga.h
+generic-y += math128.h
arch/cris/include/asm/Kbuild +1 -0
index 956eea2..6f7f36c 100644
@@ -8,3 +8,4 @@ header-y += etraxgpio.h
 header-y += rs485.h
 header-y += rtc.h
 header-y += sync_serial.h
+generic-y += math128.h
arch/frv/include/asm/Kbuild +1 -0
index 5be6663..8430351 100644
@@ -2,3 +2,4 @@ include include/asm-generic/Kbuild.asm
 
 header-y += registers.h
 header-y += termios.h
+generic-y += math128.h
arch/h8300/include/asm/Kbuild +1 -0
index c68e168..a58a669 100644
@@ -1 +1,2 @@
 include include/asm-generic/Kbuild.asm
+generic-y += math128.h
arch/hexagon/include/asm/Kbuild +1 -0
index 9aa17f1..df1203f 100644
@@ -56,3 +56,4 @@ generic-y += types.h
 generic-y += ucontext.h
 generic-y += unaligned.h
 generic-y += xor.h
+generic-y += math128.h
arch/ia64/include/asm/Kbuild +1 -0
index 241d1c5..e95a5dc 100644
@@ -12,3 +12,4 @@ header-y += ptrace_offsets.h
 header-y += rse.h
 header-y += ucontext.h
 header-y += ustack.h
+generic-y += math128.h
arch/m32r/include/asm/Kbuild +1 -0
index c68e168..a58a669 100644
@@ -1 +1,2 @@
 include include/asm-generic/Kbuild.asm
+generic-y += math128.h
arch/m68k/include/asm/Kbuild +1 -0
index 1a922fa..1c2a318 100644
@@ -1,2 +1,3 @@
 include include/asm-generic/Kbuild.asm
 header-y += cachectl.h
+generic-y += math128.h
arch/microblaze/include/asm/Kbuild +1 -0
index db5294c..6faf1fe 100644
@@ -1,3 +1,4 @@
 include include/asm-generic/Kbuild.asm
 
 header-y  += elf.h
+generic-y += math128.h
arch/mips/include/asm/Kbuild +1 -0
index f53f9ca..c1e9992 100644
@@ -3,3 +3,4 @@ include include/asm-generic/Kbuild.asm
 header-y += cachectl.h
 header-y += sgidefs.h
 header-y += sysmips.h
+generic-y += math128.h
arch/mn10300/include/asm/Kbuild +1 -0
index c68e168..a58a669 100644
@@ -1 +1,2 @@
 include include/asm-generic/Kbuild.asm
+generic-y += math128.h
arch/openrisc/include/asm/Kbuild +1 -0
index c936483..c707d4b 100644
@@ -66,3 +66,4 @@ generic-y += topology.h
 generic-y += types.h
 generic-y += ucontext.h
 generic-y += user.h
+generic-y += math128.h
arch/parisc/include/asm/Kbuild +1 -0
index 19a434f..0663c2b 100644
@@ -1,3 +1,4 @@
 include include/asm-generic/Kbuild.asm
 
 header-y += pdc.h
+generic-y += math128.h
arch/powerpc/include/asm/Kbuild +1 -0
index 7e313f1..8565e89 100644
@@ -36,3 +36,4 @@ header-y += ucontext.h
 header-y += unistd.h
 
 generic-y += rwsem.h
+generic-y += math128.h
arch/s390/include/asm/Kbuild +1 -0
index 287d7bb..7947b7a 100644
@@ -13,3 +13,4 @@ header-y += tape390.h
 header-y += ucontext.h
 header-y += vtoc.h
 header-y += zcrypt.h
+generic-y += math128.h
arch/score/include/asm/Kbuild +1 -0
index b367abd..dba713d 100644
@@ -1,3 +1,4 @@
 include include/asm-generic/Kbuild.asm
 
 header-y +=
+generic-y += math128.h
arch/sh/include/asm/Kbuild +1 -0
index 7beb423..84af798 100644
@@ -9,3 +9,4 @@ header-y += ptrace_32.h
 header-y += ptrace_64.h
 header-y += unistd_32.h
 header-y += unistd_64.h
+generic-y += math128.h
arch/sparc/include/asm/Kbuild +1 -0
index 2c2e388..e153f78 100644
@@ -21,3 +21,4 @@ generic-y += div64.h
 generic-y += local64.h
 generic-y += irq_regs.h
 generic-y += local.h
+generic-y += math128.h
arch/tile/include/asm/Kbuild +1 -0
index 0bb4264..0c1257f 100644
@@ -42,3 +42,4 @@ generic-y += termios.h
 generic-y += types.h
 generic-y += ucontext.h
 generic-y += xor.h
+generic-y += math128.h
arch/um/include/asm/Kbuild +1 -0
index fff2435..7644899 100644
@@ -2,3 +2,4 @@ generic-y += bug.h cputime.h device.h emergency-restart.h futex.h hardirq.h
 generic-y += hw_irq.h irq_regs.h kdebug.h percpu.h sections.h topology.h xor.h
 generic-y += ftrace.h pci.h io.h param.h delay.h mutex.h current.h exec.h
 generic-y += switch_to.h
+generic-y += math128.h
arch/unicore32/include/asm/Kbuild +1 -0
index 34b789b..56e1880 100644
@@ -58,3 +58,4 @@ generic-y += unaligned.h
 generic-y += user.h
 generic-y += vga.h
 generic-y += xor.h
+generic-y += math128.h
arch/x86/include/asm/Kbuild +1 -0
index f9c0d3b..86441b9 100644
@@ -26,3 +26,4 @@ header-y += vsyscall.h
 genhdr-y += unistd_32.h
 genhdr-y += unistd_64.h
 genhdr-y += unistd_x32.h
+generic-y += math128.h
arch/xtensa/include/asm/Kbuild +1 -0
index c68e168..a58a669 100644
@@ -1 +1,2 @@
 include include/asm-generic/Kbuild.asm
+generic-y += math128.h
include/asm-generic/math128.h new file +4 -0
new file mode 100644
index 0000000..3582691
@@ -0,0 +1,4 @@
+#ifndef _ASM_GENERIC_MATH128_H
+#define _ASM_GENERIC_MATH128_H
+
+#endif /*_ASM_GENERIC_MATH128_H */
include/linux/math128.h new file +180 -0

180 lines not on this page · raw message

lib/Makefile +1 -1
index 74290c9..08014b4 100644
@@ -12,7 +12,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
 	 idr.o int_sqrt.o extable.o prio_tree.o \
 	 sha1.o md5.o irq_regs.o reciprocal_div.o argv_split.o \
 	 proportions.o prio_heap.o ratelimit.o show_mem.o \
-	 is_single_threaded.o plist.o decompress.o
+	 is_single_threaded.o plist.o decompress.o math128.o
 
 lib-$(CONFIG_MMU) += ioremap.o
 lib-$(CONFIG_SMP) += cpumask.o
lib/math128.c new file +40 -0
new file mode 100644
index 0000000..55b123a
@@ -0,0 +1,40 @@
+#include <linux/math128.h>
+
+#ifndef mul_u64_u64
+/*
+ * a * b = (ah * 2^32 + al) * (bh * 2^32 + bl) =
+ *   ah*bh * 2^64 + (ah*bl + bh*al) * 2^32 + al*bl
+ */
+u128 mul_u64_u64(u64 a, u64 b)
+{
+	u128 t1, t2, t3, t4;
+	u32 ah, al;
+	u32 bh, bl;
+
+	ah = a >> 32;
+	al = a;
+
+	bh = b >> 32;
+	bl = b;
+
+	t1.lo = 0;
+	t1.hi = (u64)ah * bh;
+
+	t2.lo = (u64)ah * bl;
+	t2.hi = t2.lo >> 32;
+	t2.lo <<= 32;
+
+	t3.lo = (u64)al * bh;
+	t3.hi = t3.lo >> 32;
+	t3.lo <<= 32;
+
+	t4.lo = (u64)al * bl;
+	t4.hi = 0;
+
+	t1 = add_u128(t1, t2);
+	t1 = add_u128(t1, t3);
+	t1 = add_u128(t1, t4);
+
+	return t1;
+}
+#endif /* mul_u64_u64 */
Signature · 1 line
1.7.9.5
author
#2 lore raw

PATCH 2/15 math128, x86_64: Implement {mul,add}_u128 in 64bit asm

In reply to Juri Lelli

From: Peter Zijlstra <[email protected]>

Enable __int128 usage when available, if not, provide asm versions of mul_u64_u64 and add_u128.

---
 arch/x86/include/asm/Kbuild    |    1 -
 arch/x86/include/asm/math128.h |   39 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 39 insertions(+), 1 deletion(-)
 create mode 100644 arch/x86/include/asm/math128.h
arch/x86/include/asm/Kbuild +0 -1
index 86441b9..f9c0d3b 100644
@@ -26,4 +26,3 @@ header-y += vsyscall.h
 genhdr-y += unistd_32.h
 genhdr-y += unistd_64.h
 genhdr-y += unistd_x32.h
-generic-y += math128.h
arch/x86/include/asm/math128.h new file +39 -0
new file mode 100644
index 0000000..c0e2a6c
@@ -0,0 +1,39 @@
+#ifndef _ASM_MATH128_H
+#define _ASM_MATH128_H
+
+#ifdef CONFIG_X86_64
+
+#ifdef __SIZEOF_INT128__
+#define ARCH_HAS_INT128
+#endif
+
+#ifndef ARCH_HAS_INT128
+
+static inline u128 mul_u64_u64(u64 a, u64 b)
+{
+       u128 res;
+
+       asm("mulq %2"
+               : "=a" (res.lo), "=d" (res.hi)
+               :  "rm" (b), "0" (a));
+
+       return res;
+}
+#define mul_u64_u64 mul_u64_u64
+
+static inline u128 add_u128(u128 a, u128 b)
+{
+       u128 res;
+
+       asm("addq %2,%0;\n"
+           "adcq %3,%1;\n"
+               : "=rm" (res.lo), "=rm" (res.hi)
+               : "r" (b.lo), "r" (b.hi), "0" (a.lo), "1" (a.hi));
+
+       return res;
+}
+#define add_u128 add_u128
+
+#endif /* ARCH_HAS_INT128 */
+#endif /* CONFIG_X86_64 */
+#endif /* _ASM_MATH128_H */
Signature · 1 line
1.7.9.5
author
#3 lore raw

PATCH 3/15 sched: add sched_class->task_dead.

In reply to Juri Lelli

From: Dario Faggioli <[email protected]>

Add a new function to the scheduling class interface. It is called at the end of a context switch, if the prev task is in TASK_DEAD state.

It might be useful for the scheduling classes that want to be notified when one of their task dies, e.g. to perform some cleanup actions.

---
 include/linux/sched.h |    1 +
 kernel/sched/core.c   |    3 +++
 2 files changed, 4 insertions(+)
include/linux/sched.h +1 -0
index 28fa9d0..6f66117 100644
@@ -1103,6 +1103,7 @@ struct sched_class {
 	void (*set_curr_task) (struct rq *rq);
 	void (*task_tick) (struct rq *rq, struct task_struct *p, int queued);
 	void (*task_fork) (struct task_struct *p);
+	void (*task_dead) (struct task_struct *p);
 
 	void (*switched_from) (struct rq *this_rq, struct task_struct *task);
 	void (*switched_to) (struct rq *this_rq, struct task_struct *task);
kernel/sched/core.c +3 -0
index d833cc9..a622f78 100644
@@ -1962,6 +1962,9 @@ static void finish_task_switch(struct rq *rq, struct task_struct *prev)
 	if (mm)
 		mmdrop(mm);
 	if (unlikely(prev_state == TASK_DEAD)) {
+		if (prev->sched_class->task_dead)
+			prev->sched_class->task_dead(prev);
+
 		/*
 		 * Remove function-return probe instances associated with this
 		 * task and put them back on the free list.
Signature · 1 line
1.7.9.5
author
#4 lore raw

PATCH 4/15 sched: add extended scheduling interface.

In reply to Juri Lelli

From: Dario Faggioli <[email protected]>

Add the interface bits needed for supporting scheduling algorithms with extended parameters (e.g., SCHED_DEADLINE).

In general, it makes possible to specify a periodic/sporadic task, that executes for a given amount of runtime at each instance, and is scheduled according to the urgency of their own timing constraints, i.e.:
- a (maximum/typical) instance execution time,
- a minimum interval between consecutive instances,
- a time constraint by which each instance must be completed.

Thus, both the data structure that holds the scheduling parameters of the tasks and the system calls dealing with it must be extended. Unfortunately, modifying the existing struct sched_param would break the ABI and result in potentially serious compatibility issues with legacy binaries.

For these reasons, this patch:
- defines the new struct sched_param2, containing all the fields that are necessary for specifying a task in the computational model described above;
- defines and implements the new scheduling related syscalls that manipulate it, i.e., sched_setscheduler2(), sched_setparam2() and sched_getparam2().

Syscalls are introduced for x86 (32 and 64 bits) and ARM only, as a proof of concept and for developing and testing purposes. Making them available on other architectures is straightforward.

Since no "user" for these new parameters is introduced in this patch, the implementation of the new system calls is just identical to their already existing counterpart. Future patches that implement scheduling policies able to exploit the new data structure must also take care of modifying the *2() calls accordingly with their own purposes.

---
 arch/arm/include/asm/unistd.h    |    3 ++
 arch/arm/kernel/calls.S          |    3 ++
 arch/x86/syscalls/syscall_32.tbl |    3 ++
 arch/x86/syscalls/syscall_64.tbl |    3 ++
 include/linux/sched.h            |   50 +++++++++++++++++
 include/linux/syscalls.h         |    7 +++
 kernel/sched/core.c              |  110 ++++++++++++++++++++++++++++++++++++--
 7 files changed, 176 insertions(+), 3 deletions(-)
arch/arm/include/asm/unistd.h +3 -0
index 512cd14..2529fbf 100644
@@ -404,6 +404,9 @@
 #define __NR_setns			(__NR_SYSCALL_BASE+375)
 #define __NR_process_vm_readv		(__NR_SYSCALL_BASE+376)
 #define __NR_process_vm_writev		(__NR_SYSCALL_BASE+377)
+#define __NR_sched_setscheduler2	(__NR_SYSCALL_BASE+378)
+#define __NR_sched_setparam2		(__NR_SYSCALL_BASE+379)
+#define __NR_sched_getparam2		(__NR_SYSCALL_BASE+380)
 
 /*
  * The following SWIs are ARM private.
arch/arm/kernel/calls.S +3 -0
index 463ff4a..be174eb 100644
@@ -387,6 +387,9 @@
 /* 375 */	CALL(sys_setns)
 		CALL(sys_process_vm_readv)
 		CALL(sys_process_vm_writev)
+		CALL(sys_sched_setscheduler2)
+		CALL(sys_sched_setparam2)
+/* 380 */	CALL(sys_sched_getparam2)
 #ifndef syscalls_counted
 .equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls
 #define syscalls_counted
arch/x86/syscalls/syscall_32.tbl +3 -0
index 29f9f05..251f197 100644
@@ -355,3 +355,6 @@
 346	i386	setns			sys_setns
 347	i386	process_vm_readv	sys_process_vm_readv		compat_sys_process_vm_readv
 348	i386	process_vm_writev	sys_process_vm_writev		compat_sys_process_vm_writev
+349	i386	sched_setparam2		sys_sched_setparam2
+350	i386	sched_getparam2		sys_sched_getparam2
+351	i386	sched_setscheduler2	sys_sched_setscheduler2
arch/x86/syscalls/syscall_64.tbl +3 -0
index dd29a9e..74fefa1 100644
@@ -318,6 +318,9 @@
 309	common	getcpu			sys_getcpu
 310	64	process_vm_readv	sys_process_vm_readv
 311	64	process_vm_writev	sys_process_vm_writev
+312	common	sched_setparam2		sys_sched_setparam2
+313	common	sched_getparam2		sys_sched_getparam2
+314	common	sched_setscheduler2	sys_sched_setscheduler2
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
 # for native 64-bit operation.
include/linux/sched.h +50 -0

62 lines not on this page · raw message

include/linux/syscalls.h +7 -0
index 3de3acb..5f7309d 100644
@@ -38,6 +38,7 @@ struct rlimit;
 struct rlimit64;
 struct rusage;
 struct sched_param;
+struct sched_param2;
 struct sel_arg_struct;
 struct semaphore;
 struct sembuf;
@@ -328,11 +329,17 @@ asmlinkage long sys_clock_nanosleep(clockid_t which_clock, int flags,
 asmlinkage long sys_nice(int increment);
 asmlinkage long sys_sched_setscheduler(pid_t pid, int policy,
 					struct sched_param __user *param);
+asmlinkage long sys_sched_setscheduler2(pid_t pid, int policy,
+					struct sched_param2 __user *param);
 asmlinkage long sys_sched_setparam(pid_t pid,
 					struct sched_param __user *param);
+asmlinkage long sys_sched_setparam2(pid_t pid,
+					struct sched_param2 __user *param);
 asmlinkage long sys_sched_getscheduler(pid_t pid);
 asmlinkage long sys_sched_getparam(pid_t pid,
 					struct sched_param __user *param);
+asmlinkage long sys_sched_getparam2(pid_t pid,
+					struct sched_param2 __user *param);
 asmlinkage long sys_sched_setaffinity(pid_t pid, unsigned int len,
 					unsigned long __user *user_mask_ptr);
 asmlinkage long sys_sched_getaffinity(pid_t pid, unsigned int len,
kernel/sched/core.c +107 -3

155 lines not on this page · raw message

Signature · 1 line
1.7.9.5
author
#5 lore raw

PATCH 5/15 sched: SCHED_DEADLINE structures & implementation.

In reply to Juri Lelli

From: Dario Faggioli <[email protected]>

Introduces the data structures, constants and symbols needed for SCHED_DEADLINE implementation.

Core data structure of SCHED_DEADLINE are defined, along with their initializers. Hooks for checking if a task belong to the new policy are also added where they are needed.

Adds a scheduling class, in sched/dl.c and a new policy called SCHED_DEADLINE. It is an implementation of the Earliest Deadline First (EDF) scheduling algorithm, augmented with a mechanism (called Constant Bandwidth Server, CBS) that makes it possible to isolate the behaviour of tasks between each other.

The typical -deadline task will be made up of a computation phase (instance) which is activated on a periodic or sporadic fashion. The expected (maximum) duration of such computation is called the task's runtime; the time interval by which each instance need to be completed is called the task's relative deadline. The task's absolute deadline is dynamically calculated as the time instant a task (better, an instance) activates plus the relative deadline.

The EDF algorithms selects the task with the smallest absolute deadline as the one to be executed first, while the CBS ensures each task to run for at most its runtime every (relative) deadline length time interval, avoiding any interference between different tasks (bandwidth isolation).
Thanks to this feature, also tasks that do not strictly comply with the computational model sketched above can effectively use the new policy.

To summarize, this patch:
- introduces the data structures, constants and symbols needed;
- implements the core logic of the scheduling algorithm in the new scheduling class file;
- provides all the glue code between the new scheduling class and the core scheduler and refines the interactions between sched/dl and the other existing scheduling classes.

---
 include/linux/sched.h    |   70 ++++-
 kernel/fork.c            |    4 +-
 kernel/hrtimer.c         |    2 +-
 kernel/sched/Makefile    |    2 +-
 kernel/sched/core.c      |  111 ++++++--
 kernel/sched/dl.c        |  688 ++++++++++++++++++++++++++++++++++++++++++++++
 kernel/sched/sched.h     |   26 ++
 kernel/sched/stop_task.c |    2 +-
 8 files changed, 883 insertions(+), 22 deletions(-)
 create mode 100644 kernel/sched/dl.c
include/linux/sched.h +68 -2
index 6d0c098..c09db62 100644
@@ -39,6 +39,7 @@
 #define SCHED_BATCH		3
 /* SCHED_ISO: reserved but not implemented yet */
 #define SCHED_IDLE		5
+#define SCHED_DEADLINE		6
 /* Can be ORed in to make sure the process is reverted back to SCHED_NORMAL on fork */
 #define SCHED_RESET_ON_FORK     0x40000000
 
@@ -130,6 +131,10 @@ struct sched_param {
  * timing constraints.
  *
  * @__unused		padding to allow future expansion without ABI issues
+ *
+ * As of now, the SCHED_DEADLINE policy (sched_dl scheduling class) is the
+ * only user of this new interface. More information about the algorithm
+ * available in the scheduling class file or in Documentation/.
  */
 struct sched_param2 {
 	int sched_priority;
@@ -1117,6 +1122,7 @@ struct sched_domain;
 #else
 #define ENQUEUE_WAKING		0
 #endif
+#define ENQUEUE_REPLENISH	8
 
 #define DEQUEUE_SLEEP		1
 
@@ -1248,6 +1254,47 @@ struct sched_rt_entity {
 #endif
 };
 
+struct sched_dl_entity {
+	struct rb_node	rb_node;
+	int nr_cpus_allowed;
+
+	/*
+	 * Original scheduling parameters. Copied here from sched_param2
+	 * during sched_setscheduler2(), they will remain the same until
+	 * the next sched_setscheduler2().
+	 */
+	u64 dl_runtime;		/* maximum runtime for each instance	*/
+	u64 dl_deadline;	/* relative deadline of each instance	*/
+
+	/*
+	 * Actual scheduling parameters. Initialized with the values above,
+	 * they are continously updated during task execution. Note that
+	 * the remaining runtime could be < 0 in case we are in overrun.
+	 */
+	s64 runtime;		/* remaining runtime for this instance	*/
+	u64 deadline;		/* absolute deadline for this instance	*/
+	unsigned int flags;	/* specifying the scheduler behaviour	*/
+
+	/*
+	 * Some bool flags:
+	 *
+	 * @dl_throttled tells if we exhausted the runtime. If so, the
+	 * task has to wait for a replenishment to be performed at the
+	 * next firing of dl_timer.
+	 *
+	 * @dl_new tells if a new instance arrived. If so we must
+	 * start executing it with full runtime and reset its absolute
+	 * deadline;
+	 */
+	int dl_throttled, dl_new;
+
+	/*
+	 * Bandwidth enforcement timer. Each -deadline task has its
+	 * own bandwidth to be enforced, thus we need one timer per task.
+	 */
+	struct hrtimer dl_timer;
+};
+
 /*
  * default timeslice is 100 msecs (used only for SCHED_RR tasks).
  * Timeslices get refilled after they expire.
@@ -1281,6 +1328,7 @@ struct task_struct {
 	const struct sched_class *sched_class;
 	struct sched_entity se;
 	struct sched_rt_entity rt;
+	struct sched_dl_entity dl;
 
 #ifdef CONFIG_PREEMPT_NOTIFIERS
 	/* list of struct preempt_notifier: */
@@ -1637,6 +1685,10 @@ struct task_struct {
  * user-space.  This allows kernel threads to set their
  * priority to a value higher than any user task. Note:
  * MAX_RT_PRIO must not be smaller than MAX_USER_RT_PRIO.
+ *
+ * SCHED_DEADLINE tasks has negative priorities, reflecting
+ * the fact that any of them has higher prio than RT and
+ * NORMAL/BATCH tasks.
  */
 
 #define MAX_USER_RT_PRIO	100
@@ -1645,9 +1697,23 @@ struct task_struct {
 #define MAX_PRIO		(MAX_RT_PRIO + 40)
 #define DEFAULT_PRIO		(MAX_RT_PRIO + 20)
 
+#define MAX_DL_PRIO		0
+
+static inline int dl_prio(int prio)
+{
+	if (unlikely(prio < MAX_DL_PRIO))
+		return 1;
+	return 0;
+}
+
+static inline int dl_task(struct task_struct *p)
+{
+	return dl_prio(p->prio);
+}
+
 static inline int rt_prio(int prio)
 {
-	if (unlikely(prio < MAX_RT_PRIO))
+	if ((unsigned)prio < MAX_RT_PRIO)
 		return 1;
 	return 0;
 }
@@ -2214,7 +2280,7 @@ extern void wake_up_new_task(struct task_struct *tsk);
 #else
  static inline void kick_process(struct task_struct *tsk) { }
 #endif
-extern void sched_fork(struct task_struct *p);
+extern int sched_fork(struct task_struct *p);
 extern void sched_dead(struct task_struct *p);
 
 extern void proc_caches_init(void);
kernel/fork.c +3 -1
index ad54c83..b04c471 100644
@@ -1310,7 +1310,9 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 #endif
 
 	/* Perform scheduler related setup. Assign this task to a CPU. */
-	sched_fork(p);
+	retval = sched_fork(p);
+	if (retval)
+		goto bad_fork_cleanup_policy;
 
 	retval = perf_event_init_task(p);
 	if (retval)
kernel/hrtimer.c +1 -1
index ae34bf5..2187152 100644
@@ -1565,7 +1565,7 @@ long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp,
 	unsigned long slack;
 
 	slack = current->timer_slack_ns;
-	if (rt_task(current))
+	if (dl_task(current) || rt_task(current))
 		slack = 0;
 
 	hrtimer_init_on_stack(&t.timer, clockid, mode);
kernel/sched/Makefile +1 -1
index 173ea52..73aa1a3 100644
@@ -11,7 +11,7 @@ ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y)
 CFLAGS_core.o := $(PROFILING) -fno-omit-frame-pointer
 endif
 
-obj-y += core.o clock.o idle_task.o fair.o rt.o stop_task.o
+obj-y += core.o clock.o idle_task.o fair.o rt.o dl.o stop_task.o
 obj-$(CONFIG_SMP) += cpupri.o
 obj-$(CONFIG_SCHED_AUTOGROUP) += auto_group.o
 obj-$(CONFIG_SCHEDSTATS) += stats.o
kernel/sched/core.c +95 -16

240 lines not on this page · raw message

kernel/sched/dl.c new file +688 -0

688 lines not on this page · raw message

kernel/sched/sched.h +26 -0
index ba9dccf..846a352 100644
@@ -50,11 +50,23 @@ static inline int rt_policy(int policy)
 	return 0;
 }
 
+static inline int dl_policy(int policy)
+{
+	if (unlikely(policy == SCHED_DEADLINE))
+		return 1;
+	return 0;
+}
+
 static inline int task_has_rt_policy(struct task_struct *p)
 {
 	return rt_policy(p->policy);
 }
 
+static inline int task_has_dl_policy(struct task_struct *p)
+{
+	return dl_policy(p->policy);
+}
+
 /*
  * This is the priority-queue data structure of the RT scheduling class:
  */
@@ -309,6 +321,15 @@ struct rt_rq {
 #endif
 };
 
+/* Deadline class' related fields in a runqueue */
+struct dl_rq {
+	/* runqueue is an rbtree, ordered by deadline */
+	struct rb_root rb_root;
+	struct rb_node *rb_leftmost;
+
+	unsigned long dl_nr_running;
+};
+
 #ifdef CONFIG_SMP
 
 /*
@@ -370,6 +391,7 @@ struct rq {
 
 	struct cfs_rq cfs;
 	struct rt_rq rt;
+	struct dl_rq dl;
 
 #ifdef CONFIG_FAIR_GROUP_SCHED
 	/* list of leaf cfs_rq on this cpu: */
@@ -844,6 +866,7 @@ enum cpuacct_stat_index {
    for (class = sched_class_highest; class; class = class->next)
 
 extern const struct sched_class stop_sched_class;
+extern const struct sched_class dl_sched_class;
 extern const struct sched_class rt_sched_class;
 extern const struct sched_class fair_sched_class;
 extern const struct sched_class idle_sched_class;
@@ -876,6 +899,8 @@ extern void resched_cpu(int cpu);
 extern struct rt_bandwidth def_rt_bandwidth;
 extern void init_rt_bandwidth(struct rt_bandwidth *rt_b, u64 period, u64 runtime);
 
+extern void init_dl_task_timer(struct sched_dl_entity *dl_se);
+
 extern void update_idle_cpu_load(struct rq *this_rq);
 
 #ifdef CONFIG_CGROUP_CPUACCT
@@ -1143,6 +1168,7 @@ extern void print_rt_stats(struct seq_file *m, int cpu);
 
 extern void init_cfs_rq(struct cfs_rq *cfs_rq);
 extern void init_rt_rq(struct rt_rq *rt_rq, struct rq *rq);
+extern void init_dl_rq(struct dl_rq *rt_rq, struct rq *rq);
 extern void unthrottle_offline_cfs_rqs(struct rq *rq);
 
 extern void account_cfs_bandwidth_used(int enabled, int was_enabled);
kernel/sched/stop_task.c +1 -1
index 7b386e8..1db3750 100644
@@ -83,7 +83,7 @@ get_rr_interval_stop(struct rq *rq, struct task_struct *task)
  * Simple, special scheduling class for the per-CPU stop tasks:
  */
 const struct sched_class stop_sched_class = {
-	.next			= &rt_sched_class,
+	.next			= &dl_sched_class,
 
 	.enqueue_task		= enqueue_task_stop,
 	.dequeue_task		= dequeue_task_stop,
Signature · 1 line
1.7.9.5
author
#6 lore raw

PATCH 6/15 sched: SCHED_DEADLINE SMP-related data structures & logic.

In reply to Juri Lelli

Introduces data structures relevant for implementing dynamic migration of -deadline tasks and the logic for checking if runqueues are overloaded with -deadline tasks and for choosing where a task should migrate, when it is the case.

Adds also dynamic migrations to SCHED_DEADLINE, so that tasks can be moved among CPUs when necessary. It is also possible to bind a task to a (set of) CPU(s), thus restricting its capability of migrating, or forbidding migrations at all.

The very same approach used in sched_rt is utilised:
- -deadline tasks are kept into CPU-specific runqueues,
- -deadline tasks are migrated among runqueues to achieve the following:
* on an M-CPU system the M earliest deadline ready tasks are always running;
* affinity/cpusets settings of all the -deadline tasks is always respected.

Therefore, this very special form of "load balancing" is done with an active method, i.e., the scheduler pushes or pulls tasks between runqueues when they are woken up and/or (de)scheduled.
IOW, every time a preemption occurs, the descheduled task might be sent to some other CPU (depending on its deadline) to continue executing (push). On the other hand, every time a CPU becomes idle, it might pull the second earliest deadline ready task from some other CPU.

To enforce this, a pull operation is always attempted before taking any scheduling decision (pre_schedule()), as well as a push one after each scheduling decision (post_schedule()). In addition, when a task arrives or wakes up, the best CPU where to resume it is selected taking into account its affinity mask, the system topology, but also its deadline. E.g., from the scheduling point of view, the best CPU where to wake up (and also where to push) a task is the one which is running the task with the latest deadline among the M executing ones.

In order to facilitate these decisions, per-runqueue "caching" of the deadlines of the currently running and of the first ready task is used. Queued but not running tasks are also parked in another rb-tree to speed-up pushes.

---
 include/linux/sched.h |    1 +
 kernel/sched/core.c   |   10 +-
 kernel/sched/dl.c     |  936 +++++++++++++++++++++++++++++++++++++++++++++++--
 kernel/sched/rt.c     |    2 +-
 kernel/sched/sched.h  |   33 ++
 5 files changed, 952 insertions(+), 30 deletions(-)
include/linux/sched.h +1 -0
index c09db62..fb8062c 100644
@@ -1370,6 +1370,7 @@ struct task_struct {
 	struct list_head tasks;
 #ifdef CONFIG_SMP
 	struct plist_node pushable_tasks;
+	struct rb_node pushable_dl_tasks;
 #endif
 
 	struct mm_struct *mm, *active_mm;
kernel/sched/core.c +9 -1
index 5fa0338..051e609 100644
@@ -1811,6 +1811,7 @@ int sched_fork(struct task_struct *p)
 #endif
 #ifdef CONFIG_SMP
 	plist_node_init(&p->pushable_tasks, MAX_PRIO);
+	RB_CLEAR_NODE(&p->pushable_dl_tasks);
 #endif
 
 	put_cpu();
@@ -5164,6 +5165,7 @@ void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask)
 
 	cpumask_copy(&p->cpus_allowed, new_mask);
 	p->rt.nr_cpus_allowed = cpumask_weight(new_mask);
+	p->dl.nr_cpus_allowed = cpumask_weight(new_mask);
 }
 
 /*
@@ -5880,6 +5882,7 @@ static void free_rootdomain(struct rcu_head *rcu)
 	struct root_domain *rd = container_of(rcu, struct root_domain, rcu);
 
 	cpupri_cleanup(&rd->cpupri);
+	free_cpumask_var(rd->dlo_mask);
 	free_cpumask_var(rd->rto_mask);
 	free_cpumask_var(rd->online);
 	free_cpumask_var(rd->span);
@@ -5931,8 +5934,10 @@ static int init_rootdomain(struct root_domain *rd)
 		goto out;
 	if (!alloc_cpumask_var(&rd->online, GFP_KERNEL))
 		goto free_span;
-	if (!alloc_cpumask_var(&rd->rto_mask, GFP_KERNEL))
+	if (!alloc_cpumask_var(&rd->dlo_mask, GFP_KERNEL))
 		goto free_online;
+	if (!alloc_cpumask_var(&rd->rto_mask, GFP_KERNEL))
+		goto free_dlo_mask;
 
 	if (cpupri_init(&rd->cpupri) != 0)
 		goto free_rto_mask;
@@ -5940,6 +5945,8 @@ static int init_rootdomain(struct root_domain *rd)
 
 free_rto_mask:
 	free_cpumask_var(rd->rto_mask);
+free_dlo_mask:
+	free_cpumask_var(rd->dlo_mask);
 free_online:
 	free_cpumask_var(rd->online);
 free_span:
@@ -7060,6 +7067,7 @@ void __init sched_init_smp(void)
 	free_cpumask_var(non_isolated_cpus);
 
 	init_sched_rt_class();
+	init_sched_dl_class();
 }
 #else
 void __init sched_init_smp(void)
kernel/sched/dl.c +908 -28

1,056 lines not on this page · raw message

kernel/sched/rt.c +1 -1
index c5565c3..ce4f5e1 100644
@@ -1794,7 +1794,7 @@ static void task_woken_rt(struct rq *rq, struct task_struct *p)
 	    !test_tsk_need_resched(rq->curr) &&
 	    has_pushable_tasks(rq) &&
 	    p->rt.nr_cpus_allowed > 1 &&
-	    rt_task(rq->curr) &&
+	    (dl_task(rq->curr) || rt_task(rq->curr)) &&
 	    (rq->curr->rt.nr_cpus_allowed < 2 ||
 	     rq->curr->prio <= p->prio))
 		push_rt_tasks(rq);
kernel/sched/sched.h +33 -0
index 846a352..4b02092 100644
@@ -328,6 +328,31 @@ struct dl_rq {
 	struct rb_node *rb_leftmost;
 
 	unsigned long dl_nr_running;
+
+#ifdef CONFIG_SMP
+	/*
+	 * Deadline values of the currently executing and the
+	 * earliest ready task on this rq. Caching these facilitates
+	 * the decision wether or not a ready but not running task
+	 * should migrate somewhere else.
+	 */
+	struct {
+		u64 curr;
+		u64 next;
+	} earliest_dl;
+
+	unsigned long dl_nr_migratory;
+	unsigned long dl_nr_total;
+	int overloaded;
+
+	/*
+	 * Tasks on this rq that can be pushed away. They are kept in
+	 * an rb-tree, ordered by tasks' deadlines, with caching
+	 * of the leftmost (earliest deadline) element.
+	 */
+	struct rb_root pushable_dl_tasks_root;
+	struct rb_node *pushable_dl_tasks_leftmost;
+#endif
 };
 
 #ifdef CONFIG_SMP
@@ -348,6 +373,13 @@ struct root_domain {
 	cpumask_var_t online;
 
 	/*
+	 * The bit corresponding to a CPU gets set here if such CPU has more
+	 * than one runnable -deadline task (as it is below for RT tasks).
+	 */
+	cpumask_var_t dlo_mask;
+	atomic_t dlo_count;
+
+	/*
 	 * The "RT overload" flag: it gets set if a CPU has more than
 	 * one runnable RT task.
 	 */
@@ -890,6 +922,7 @@ extern void sched_init_granularity(void);
 extern void update_max_interval(void);
 extern void update_group_power(struct sched_domain *sd, int cpu);
 extern int update_runtime(struct notifier_block *nfb, unsigned long action, void *hcpu);
+extern void init_sched_dl_class(void);
 extern void init_sched_rt_class(void);
 extern void init_sched_fair_class(void);
 
Signature · 1 line
1.7.9.5
author
#7 lore raw

PATCH 7/15 sched: SCHED_DEADLINE avg_update accounting.

In reply to Juri Lelli

From: Dario Faggioli <[email protected]>

Make the core scheduler and load balancer aware of the load produced by -deadline tasks, by updating the moving average like for sched_rt.

---
 kernel/sched/dl.c |    2 ++
 1 file changed, 2 insertions(+)
kernel/sched/dl.c +2 -0
index 9bc48a0..0262c16 100644
@@ -554,6 +554,8 @@ static void update_curr_dl(struct rq *rq)
 	curr->se.exec_start = rq->clock_task;
 	cpuacct_charge(curr, delta_exec);
 
+	sched_rt_avg_update(rq, delta_exec);
+
 	dl_se->runtime -= delta_exec;
 	if (dl_runtime_exceeded(rq, dl_se)) {
 		__dequeue_task_dl(rq, curr, 0);
Signature · 1 line
1.7.9.5
author
#8 lore raw

PATCH 8/15 sched: add period support for -deadline tasks.

In reply to Juri Lelli

From: Harald Gustafsson <[email protected]>

Make it possible to specify a period (different or equal than deadline) for -deadline tasks.

---
 include/linux/sched.h |    1 +
 kernel/sched/core.c   |   15 ++++++++++++---
 kernel/sched/dl.c     |   10 +++++++---
 3 files changed, 20 insertions(+), 6 deletions(-)
include/linux/sched.h +1 -0
index fb8062c..dc5b753 100644
@@ -1265,6 +1265,7 @@ struct sched_dl_entity {
 	 */
 	u64 dl_runtime;		/* maximum runtime for each instance	*/
 	u64 dl_deadline;	/* relative deadline of each instance	*/
+	u64 dl_period;		/* separation of two instances (period) */
 
 	/*
 	 * Actual scheduling parameters. Initialized with the values above,
kernel/sched/core.c +12 -3
index 051e609..fd09961 100644
@@ -1724,6 +1724,7 @@ static void __sched_fork(struct task_struct *p)
 	hrtimer_init(&p->dl.dl_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
 	p->dl.dl_runtime = p->dl.runtime = 0;
 	p->dl.dl_deadline = p->dl.deadline = 0;
+	p->dl.dl_period = 0;
 	p->dl.flags = 0;
 
 	INIT_LIST_HEAD(&p->rt.run_list);
@@ -4092,6 +4093,10 @@ __setparam_dl(struct task_struct *p, const struct sched_param2 *param2)
 	init_dl_task_timer(dl_se);
 	dl_se->dl_runtime = param2->sched_runtime;
 	dl_se->dl_deadline = param2->sched_deadline;
+	if (param2->sched_period != 0)
+		dl_se->dl_period = param2->sched_period;
+	else
+		dl_se->dl_period = dl_se->dl_deadline;
 	dl_se->flags = param2->sched_flags;
 	dl_se->dl_throttled = 0;
 	dl_se->dl_new = 1;
@@ -4105,19 +4110,23 @@ __getparam_dl(struct task_struct *p, struct sched_param2 *param2)
 	param2->sched_priority = p->rt_priority;
 	param2->sched_runtime = dl_se->dl_runtime;
 	param2->sched_deadline = dl_se->dl_deadline;
+	param2->sched_period = dl_se->dl_period;
 	param2->sched_flags = dl_se->flags;
 }
 
 /*
  * This function validates the new parameters of a -deadline task.
  * We ask for the deadline not being zero, and greater or equal
- * than the runtime.
+ * than the runtime, as well as the period of being zero or
+ * greater than deadline.
  */
 static bool
 __checkparam_dl(const struct sched_param2 *prm)
 {
-	return prm && (&prm->sched_deadline) != 0 &&
-	       (s64)(&prm->sched_deadline - &prm->sched_runtime) >= 0;
+	return prm && prm->sched_deadline != 0 &&
+	       (prm->sched_period == 0 ||
+		(s64)(prm->sched_period - prm->sched_deadline) >= 0) &&
+	       (s64)(prm->sched_deadline - prm->sched_runtime) >= 0;
 }
 
 /*
kernel/sched/dl.c +7 -3
index 0262c16..75de7cc 100644
@@ -286,7 +286,7 @@ static void replenish_dl_entity(struct sched_dl_entity *dl_se)
 	 * arbitrary large.
 	 */
 	while (dl_se->runtime <= 0) {
-		dl_se->deadline += dl_se->dl_deadline;
+		dl_se->deadline += dl_se->dl_period;
 		dl_se->runtime += dl_se->dl_runtime;
 	}
 
@@ -326,9 +326,13 @@ static void replenish_dl_entity(struct sched_dl_entity *dl_se)
  *
  * This function returns true if:
  *
- *   runtime / (deadline - t) > dl_runtime / dl_deadline ,
+ *   runtime / (deadline - t) > dl_runtime / dl_period ,
  *
  * IOW we can't recycle current parameters.
+ *
+ * Notice that the bandwidth check is done against the period. For
+ * task with deadline equal to period this is the same of using
+ * dl_deadline instead of dl_period in the equation above.
  */
 static bool dl_entity_overflow(struct sched_dl_entity *dl_se, u64 t)
 {
@@ -347,7 +351,7 @@ static bool dl_entity_overflow(struct sched_dl_entity *dl_se, u64 t)
 	 * to the (absolute) deadline. Therefore, overflowing the u64
 	 * type is very unlikely to occur in both cases.
 	 */
-	left = mul_u64_u64(dl_se->dl_deadline, dl_se->runtime);
+	left = mul_u64_u64(dl_se->dl_period, dl_se->runtime);
 	right = mul_u64_u64((dl_se->deadline - t), dl_se->dl_runtime);
 
 	if (cmp_u128(left, right) > 0)
Signature · 1 line
1.7.9.5
author
#9 lore raw

PATCH 9/15 sched: add schedstats for -deadline tasks.

In reply to Juri Lelli

From: Dario Faggioli <[email protected]>

Add some typical sched-debug output to dl_rq(s) and some schedstats to -deadline tasks.

---
 include/linux/sched.h |   13 +++++++++++++
 kernel/sched/debug.c  |   46 ++++++++++++++++++++++++++++++++++++++++++++++
 kernel/sched/dl.c     |   45 +++++++++++++++++++++++++++++++++++++++++++++
 kernel/sched/sched.h  |    9 ++++++++-
 4 files changed, 112 insertions(+), 1 deletion(-)
include/linux/sched.h +13 -0
index dc5b753..3599db7 100644
@@ -1254,6 +1254,15 @@ struct sched_rt_entity {
 #endif
 };
 
+#ifdef CONFIG_SCHEDSTATS
+struct sched_stats_dl {
+	u64			last_dmiss;
+	u64			last_rorun;
+	u64			dmiss_max;
+	u64			rorun_max;
+};
+#endif
+
 struct sched_dl_entity {
 	struct rb_node	rb_node;
 	int nr_cpus_allowed;
@@ -1294,6 +1303,10 @@ struct sched_dl_entity {
 	 * own bandwidth to be enforced, thus we need one timer per task.
 	 */
 	struct hrtimer dl_timer;
+
+#ifdef CONFIG_SCHEDSTATS
+	struct sched_stats_dl stats;
+#endif
 };
 
 /*
kernel/sched/debug.c +46 -0

64 lines not on this page · raw message

kernel/sched/dl.c +45 -0

66 lines not on this page · raw message

kernel/sched/sched.h +8 -1
index 4b02092..996a992 100644
@@ -329,6 +329,8 @@ struct dl_rq {
 
 	unsigned long dl_nr_running;
 
+	u64 exec_clock;
+
 #ifdef CONFIG_SMP
 	/*
 	 * Deadline values of the currently executing and the
@@ -355,6 +357,11 @@ struct dl_rq {
 #endif
 };
 
+#ifdef CONFIG_SCHED_DEBUG
+struct sched_dl_entity *__pick_dl_last_entity(struct dl_rq *dl_rq);
+void print_dl_stats(struct seq_file *m, int cpu);
+#endif
+
 #ifdef CONFIG_SMP
 
 /*
@@ -1201,7 +1208,7 @@ extern void print_rt_stats(struct seq_file *m, int cpu);
 
 extern void init_cfs_rq(struct cfs_rq *cfs_rq);
 extern void init_rt_rq(struct rt_rq *rt_rq, struct rq *rq);
-extern void init_dl_rq(struct dl_rq *rt_rq, struct rq *rq);
+extern void init_dl_rq(struct dl_rq *dl_rq, struct rq *rq);
 extern void unthrottle_offline_cfs_rqs(struct rq *rq);
 
 extern void account_cfs_bandwidth_used(int enabled, int was_enabled);
Signature · 1 line
1.7.9.5
author
#10 lore raw

PATCH 10/15 sched: add latency tracing for -deadline tasks.

In reply to Juri Lelli

From: Dario Faggioli <[email protected]>

It is very likely that systems that wants/needs to use the new SCHED_DEADLINE policy also want to have the scheduling latency of the -deadline tasks under control.

For this reason a new version of the scheduling wakeup latency, called "wakeup_dl", is introduced.

As a consequence of applying this patch there will be three wakeup latency tracer:
* "wakeup", that deals with all tasks in the system;
* "wakeup_rt", that deals with -rt and -deadline tasks only;
* "wakeup_dl", that deals with -deadline tasks only.

---
 kernel/trace/trace_sched_wakeup.c |   44 +++++++++++++++++++++++++++++++++----
 kernel/trace/trace_selftest.c     |   30 +++++++++++++++----------
 2 files changed, 58 insertions(+), 16 deletions(-)
kernel/trace/trace_sched_wakeup.c +40 -4
index ff791ea..e37da43 100644
@@ -27,6 +27,7 @@ static int			wakeup_cpu;
 static int			wakeup_current_cpu;
 static unsigned			wakeup_prio = -1;
 static int			wakeup_rt;
+static int			wakeup_dl;
 
 static arch_spinlock_t wakeup_lock =
 	(arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;
@@ -428,9 +429,17 @@ probe_wakeup(void *ignore, struct task_struct *p, int success)
 	tracing_record_cmdline(p);
 	tracing_record_cmdline(current);
 
-	if ((wakeup_rt && !rt_task(p)) ||
-			p->prio >= wakeup_prio ||
-			p->prio >= current->prio)
+	/*
+	 * Semantic is like this:
+	 *  - wakeup tracer handles all tasks in the system, independently
+	 *    from their scheduling class;
+	 *  - wakeup_rt tracer handles tasks belonging to sched_dl and
+	 *    sched_rt class;
+	 *  - wakeup_dl handles tasks belonging to sched_dl class only.
+	 */
+	if ((wakeup_dl && !dl_task(p)) ||
+	    (wakeup_rt && !dl_task(p) && !rt_task(p)) ||
+	    (p->prio >= wakeup_prio || p->prio >= current->prio))
 		return;
 
 	pc = preempt_count();
@@ -442,7 +451,7 @@ probe_wakeup(void *ignore, struct task_struct *p, int success)
 	arch_spin_lock(&wakeup_lock);
 
 	/* check for races. */
-	if (!tracer_enabled || p->prio >= wakeup_prio)
+	if (!tracer_enabled || (!dl_task(p) && p->prio >= wakeup_prio))
 		goto out_locked;
 
 	/* reset the trace */
@@ -550,16 +559,25 @@ static int __wakeup_tracer_init(struct trace_array *tr)
 
 static int wakeup_tracer_init(struct trace_array *tr)
 {
+	wakeup_dl = 0;
 	wakeup_rt = 0;
 	return __wakeup_tracer_init(tr);
 }
 
 static int wakeup_rt_tracer_init(struct trace_array *tr)
 {
+	wakeup_dl = 0;
 	wakeup_rt = 1;
 	return __wakeup_tracer_init(tr);
 }
 
+static int wakeup_dl_tracer_init(struct trace_array *tr)
+{
+	wakeup_dl = 1;
+	wakeup_rt = 0;
+	return __wakeup_tracer_init(tr);
+}
+
 static void wakeup_tracer_reset(struct trace_array *tr)
 {
 	stop_wakeup_tracer(tr);
@@ -622,6 +640,20 @@ static struct tracer wakeup_rt_tracer __read_mostly =
 	.use_max_tr	= 1,
 };
 
+static struct tracer wakeup_dl_tracer __read_mostly =
+{
+	.name		= "wakeup_dl",
+	.init		= wakeup_dl_tracer_init,
+	.reset		= wakeup_tracer_reset,
+	.start		= wakeup_tracer_start,
+	.stop		= wakeup_tracer_stop,
+	.wait_pipe	= poll_wait_pipe,
+	.print_max	= 1,
+#ifdef CONFIG_FTRACE_SELFTEST
+	.selftest    = trace_selftest_startup_wakeup,
+#endif
+};
+
 __init static int init_wakeup_tracer(void)
 {
 	int ret;
@@ -634,6 +666,10 @@ __init static int init_wakeup_tracer(void)
 	if (ret)
 		return ret;
 
+	ret = register_tracer(&wakeup_dl_tracer);
+	if (ret)
+		return ret;
+
 	return 0;
 }
 device_initcall(init_wakeup_tracer);
kernel/trace/trace_selftest.c +18 -12
index 288541f..849063e 100644
@@ -765,11 +765,17 @@ trace_selftest_startup_nop(struct tracer *trace, struct trace_array *tr)
 #ifdef CONFIG_SCHED_TRACER
 static int trace_wakeup_test_thread(void *data)
 {
-	/* Make this a RT thread, doesn't need to be too high */
-	static const struct sched_param param = { .sched_priority = 5 };
+	/* Make this a -deadline thread */
+	struct sched_param2 paramx = {
+		.sched_priority = 0,
+		.sched_runtime = 100000ULL,
+		.sched_deadline = 10000000ULL,
+		.sched_period = 10000000ULL
+		.sched_flags = 0
+	};
 	struct completion *x = data;
 
-	sched_setscheduler(current, SCHED_FIFO, &param);
+	sched_setscheduler2(current, SCHED_DEADLINE, &paramx);
 
 	/* Make it know we have a new prio */
 	complete(x);
@@ -781,8 +787,8 @@ static int trace_wakeup_test_thread(void *data)
 	/* we are awake, now wait to disappear */
 	while (!kthread_should_stop()) {
 		/*
-		 * This is an RT task, do short sleeps to let
-		 * others run.
+		 * This will likely be the system top priority
+		 * task, do short sleeps to let others run.
 		 */
 		msleep(100);
 	}
@@ -795,21 +801,21 @@ trace_selftest_startup_wakeup(struct tracer *trace, struct trace_array *tr)
 {
 	unsigned long save_max = tracing_max_latency;
 	struct task_struct *p;
-	struct completion isrt;
+	struct completion is_ready;
 	unsigned long count;
 	int ret;
 
-	init_completion(&isrt);
+	init_completion(&is_ready);
 
-	/* create a high prio thread */
-	p = kthread_run(trace_wakeup_test_thread, &isrt, "ftrace-test");
+	/* create a -deadline thread */
+	p = kthread_run(trace_wakeup_test_thread, &is_ready, "ftrace-test");
 	if (IS_ERR(p)) {
 		printk(KERN_CONT "Failed to create ftrace wakeup test thread ");
 		return -1;
 	}
 
-	/* make sure the thread is running at an RT prio */
-	wait_for_completion(&isrt);
+	/* make sure the thread is running at -deadline policy */
+	wait_for_completion(&is_ready);
 
 	/* start the tracing */
 	ret = tracer_init(trace, tr);
@@ -821,7 +827,7 @@ trace_selftest_startup_wakeup(struct tracer *trace, struct trace_array *tr)
 	/* reset the max latency */
 	tracing_max_latency = 0;
 
-	/* sleep to let the RT thread sleep too */
+	/* sleep to let the thread sleep too */
 	msleep(100);
 
 	/*
Signature · 1 line
1.7.9.5
author
#11 lore raw

PATCH 11/15 rtmutex: turn the plist into an rb-tree.

In reply to Juri Lelli

From: Peter Zijlstra <[email protected]>

Turn the pi-chains from plist to rb-tree, in the rt_mutex code, and provide a proper comparison function for -deadline and -priority tasks.

This is done mainly because:
- classical prio field of the plist is just an int, which might not be enough for representing a deadline;
- manipulating such a list would become O(nr_deadline_tasks), which might be to much, as the number of -deadline task increases.

Therefore, an rb-tree is used, and tasks are queued in it according to the following logic:
- among two -priority (i.e., SCHED_BATCH/OTHER/RR/FIFO) tasks, the one with the higher (lower, actually!) prio wins;
- among a -priority and a -deadline task, the latter always wins;
- among two -deadline tasks, the one with the earliest deadline wins.

Queueing and dequeueing functions are changed accordingly, for both the list of a task's pi-waiters and the list of tasks blocked on a pi-lock.

---
 include/linux/init_task.h |   10 +++
 include/linux/rtmutex.h   |   18 ++----
 include/linux/sched.h     |    4 +-
 kernel/fork.c             |    3 +-
 kernel/rtmutex-debug.c    |   10 ++-
 kernel/rtmutex.c          |  150 +++++++++++++++++++++++++++++++++++----------
 kernel/rtmutex_common.h   |   22 +++----
 kernel/sched/core.c       |    4 --
 8 files changed, 155 insertions(+), 66 deletions(-)
include/linux/init_task.h +10 -0
index e4baff5..b5ab616 100644
@@ -10,6 +10,7 @@
 #include <linux/pid_namespace.h>
 #include <linux/user_namespace.h>
 #include <linux/securebits.h>
+#include <linux/rbtree.h>
 #include <net/net_namespace.h>
 
 #ifdef CONFIG_SMP
@@ -134,6 +135,14 @@ extern struct cred init_cred;
 
 #define INIT_TASK_COMM "swapper"
 
+#ifdef CONFIG_RT_MUTEXES
+# define INIT_RT_MUTEXES(tsk)						\
+	.pi_waiters = RB_ROOT,						\
+	.pi_waiters_leftmost = NULL,
+#else
+# define INIT_RT_MUTEXES(tsk)
+#endif
+
 /*
  *  INIT_TASK is used to set up the first task table, touch at
  * your own risk!. Base=0, limit=0x1fffff (=2MB)
@@ -200,6 +209,7 @@ extern struct cred init_cred;
 	INIT_TRACE_RECURSION						\
 	INIT_TASK_RCU_PREEMPT(tsk)					\
 	INIT_CPUSET_SEQ							\
+	INIT_RT_MUTEXES(tsk)						\
 }
 
 
include/linux/rtmutex.h +6 -12
index de17134..3aed8d7 100644
@@ -13,7 +13,7 @@
 #define __LINUX_RT_MUTEX_H
 
 #include <linux/linkage.h>
-#include <linux/plist.h>
+#include <linux/rbtree.h>
 #include <linux/spinlock_types.h>
 
 extern int max_lock_depth; /* for sysctl */
@@ -22,12 +22,14 @@ extern int max_lock_depth; /* for sysctl */
  * The rt_mutex structure
  *
  * @wait_lock:	spinlock to protect the structure
- * @wait_list:	pilist head to enqueue waiters in priority order
+ * @waiters:	rbtree root to enqueue waiters in priority order
+ * @waiters_leftmost: top waiter
  * @owner:	the mutex owner
  */
 struct rt_mutex {
 	raw_spinlock_t		wait_lock;
-	struct plist_head	wait_list;
+	struct rb_root          waiters;
+	struct rb_node          *waiters_leftmost;
 	struct task_struct	*owner;
 #ifdef CONFIG_DEBUG_RT_MUTEXES
 	int			save_state;
@@ -66,7 +68,7 @@ struct hrtimer_sleeper;
 
 #define __RT_MUTEX_INITIALIZER(mutexname) \
 	{ .wait_lock = __RAW_SPIN_LOCK_UNLOCKED(mutexname.wait_lock) \
-	, .wait_list = PLIST_HEAD_INIT(mutexname.wait_list) \
+	, .waiters = RB_ROOT \
 	, .owner = NULL \
 	__DEBUG_RT_MUTEX_INITIALIZER(mutexname)}
 
@@ -98,12 +100,4 @@ extern int rt_mutex_trylock(struct rt_mutex *lock);
 
 extern void rt_mutex_unlock(struct rt_mutex *lock);
 
-#ifdef CONFIG_RT_MUTEXES
-# define INIT_RT_MUTEXES(tsk)						\
-	.pi_waiters	= PLIST_HEAD_INIT(tsk.pi_waiters),	\
-	INIT_RT_MUTEX_DEBUG(tsk)
-#else
-# define INIT_RT_MUTEXES(tsk)
-#endif
-
 #endif
include/linux/sched.h +3 -1
index 3599db7..1d74c3e 100644
@@ -57,6 +57,7 @@ struct sched_param {
 #include <linux/types.h>
 #include <linux/timex.h>
 #include <linux/jiffies.h>
+#include <linux/plist.h>
 #include <linux/rbtree.h>
 #include <linux/thread_info.h>
 #include <linux/cpumask.h>
@@ -1531,7 +1532,8 @@ struct task_struct {
 
 #ifdef CONFIG_RT_MUTEXES
 	/* PI waiters blocked on a rt_mutex held by this task */
-	struct plist_head pi_waiters;
+	struct rb_root pi_waiters;
+	struct rb_node *pi_waiters_leftmost;
 	/* Deadlock detection and priority inheritance handling */
 	struct rt_mutex_waiter *pi_blocked_on;
 #endif
kernel/fork.c +2 -1
index b04c471..12c531b 100644
@@ -1111,7 +1111,8 @@ static void rt_mutex_init_task(struct task_struct *p)
 {
 	raw_spin_lock_init(&p->pi_lock);
 #ifdef CONFIG_RT_MUTEXES
-	plist_head_init(&p->pi_waiters);
+	p->pi_waiters = RB_ROOT;
+	p->pi_waiters_leftmost = NULL;
 	p->pi_blocked_on = NULL;
 #endif
 }
kernel/rtmutex-debug.c +4 -6
index 16502d3..0f339ca 100644
@@ -23,7 +23,7 @@
 #include <linux/kallsyms.h>
 #include <linux/syscalls.h>
 #include <linux/interrupt.h>
-#include <linux/plist.h>
+#include <linux/rbtree.h>
 #include <linux/fs.h>
 #include <linux/debug_locks.h>
 
@@ -56,7 +56,7 @@ static void printk_lock(struct rt_mutex *lock, int print_owner)
 
 void rt_mutex_debug_task_free(struct task_struct *task)
 {
-	DEBUG_LOCKS_WARN_ON(!plist_head_empty(&task->pi_waiters));
+	DEBUG_LOCKS_WARN_ON(!RB_EMPTY_ROOT(&task->pi_waiters));
 	DEBUG_LOCKS_WARN_ON(task->pi_blocked_on);
 }
 
@@ -153,16 +153,14 @@ void debug_rt_mutex_proxy_unlock(struct rt_mutex *lock)
 void debug_rt_mutex_init_waiter(struct rt_mutex_waiter *waiter)
 {
 	memset(waiter, 0x11, sizeof(*waiter));
-	plist_node_init(&waiter->list_entry, MAX_PRIO);
-	plist_node_init(&waiter->pi_list_entry, MAX_PRIO);
+	RB_CLEAR_NODE(&waiter->pi_tree_entry);
+	RB_CLEAR_NODE(&waiter->tree_entry);
 	waiter->deadlock_task_pid = NULL;
 }
 
 void debug_rt_mutex_free_waiter(struct rt_mutex_waiter *waiter)
 {
 	put_pid(waiter->deadlock_task_pid);
-	DEBUG_LOCKS_WARN_ON(!plist_node_empty(&waiter->list_entry));
-	DEBUG_LOCKS_WARN_ON(!plist_node_empty(&waiter->pi_list_entry));
 	memset(waiter, 0x22, sizeof(*waiter));
 }
 
kernel/rtmutex.c +119 -31

261 lines not on this page · raw message

kernel/rtmutex_common.h +11 -11
index 53a66c8..b65442f 100644
@@ -40,13 +40,13 @@ extern void schedule_rt_mutex_test(struct rt_mutex *lock);
  * This is the control structure for tasks blocked on a rt_mutex,
  * which is allocated on the kernel stack on of the blocked task.
  *
- * @list_entry:		pi node to enqueue into the mutex waiters list
- * @pi_list_entry:	pi node to enqueue into the mutex owner waiters list
+ * @tree_entry:		pi node to enqueue into the mutex waiters tree
+ * @pi_tree_entry:	pi node to enqueue into the mutex owner waiters tree
  * @task:		task reference to the blocked task
  */
 struct rt_mutex_waiter {
-	struct plist_node	list_entry;
-	struct plist_node	pi_list_entry;
+	struct rb_node          tree_entry;
+	struct rb_node          pi_tree_entry;
 	struct task_struct	*task;
 	struct rt_mutex		*lock;
 #ifdef CONFIG_DEBUG_RT_MUTEXES
@@ -57,11 +57,11 @@ struct rt_mutex_waiter {
 };
 
 /*
- * Various helpers to access the waiters-plist:
+ * Various helpers to access the waiters-tree:
  */
 static inline int rt_mutex_has_waiters(struct rt_mutex *lock)
 {
-	return !plist_head_empty(&lock->wait_list);
+	return !RB_EMPTY_ROOT(&lock->waiters);
 }
 
 static inline struct rt_mutex_waiter *
@@ -69,8 +69,8 @@ rt_mutex_top_waiter(struct rt_mutex *lock)
 {
 	struct rt_mutex_waiter *w;
 
-	w = plist_first_entry(&lock->wait_list, struct rt_mutex_waiter,
-			       list_entry);
+	w = rb_entry(lock->waiters_leftmost, struct rt_mutex_waiter,
+		     tree_entry);
 	BUG_ON(w->lock != lock);
 
 	return w;
@@ -78,14 +78,14 @@ rt_mutex_top_waiter(struct rt_mutex *lock)
 
 static inline int task_has_pi_waiters(struct task_struct *p)
 {
-	return !plist_head_empty(&p->pi_waiters);
+	return !RB_EMPTY_ROOT(&p->pi_waiters);
 }
 
 static inline struct rt_mutex_waiter *
 task_top_pi_waiter(struct task_struct *p)
 {
-	return plist_first_entry(&p->pi_waiters, struct rt_mutex_waiter,
-				  pi_list_entry);
+	return rb_entry(p->pi_waiters_leftmost, struct rt_mutex_waiter,
+			pi_tree_entry);
 }
 
 /*
kernel/sched/core.c +0 -4
index fd09961..e1f312a 100644
@@ -7245,10 +7245,6 @@ void __init sched_init(void)
 	INIT_HLIST_HEAD(&init_task.preempt_notifiers);
 #endif
 
-#ifdef CONFIG_RT_MUTEXES
-	plist_head_init(&init_task.pi_waiters);
-#endif
-
 	/*
 	 * The boot idle thread does lazy MMU switching as well:
 	 */
Signature · 1 line
1.7.9.5
author
#12 lore raw

PATCH 12/15 sched: drafted deadline inheritance logic.

In reply to Juri Lelli

From: Dario Faggioli <[email protected]>

Some method to deal with rt-mutexes and make sched_dl interact with the current PI-coded is needed, raising all but trivial issues, that needs (according to us) to be solved with some restructuring of the pi-code (i.e., going toward a proxy execution-ish implementation).

This is under development, in the meanwhile, as a temporary solution, what this commits does is:
- ensure a pi-lock owner with waiters is never throttled down. Instead, when it runs out of runtime, it immediately gets replenished and it's deadline is postponed;
- the scheduling parameters (relative deadline and default runtime) used for that replenishments --during the whole period it holds the pi-lock-- are the ones of the waiting task with earliest deadline.

Acting this way, we provide some kind of boosting to the lock-owner, still by using the existing (actually, slightly modified by the previous commit) pi-architecture.

We would stress the fact that this is only a surely needed, all but clean solution to the problem. In the end it's only a way to re-start discussion within the community. So, as always, comments, ideas, rants, etc.. are welcome! :-)

---
 include/linux/sched.h |    9 ++++-
 kernel/fork.c         |    1 +
 kernel/rtmutex.c      |   13 +++++--
 kernel/sched/core.c   |   34 +++++++++++++++---
 kernel/sched/dl.c     |   91 ++++++++++++++++++++++++++++---------------------
 kernel/sched/sched.h  |   14 ++++++++
 6 files changed, 116 insertions(+), 46 deletions(-)
include/linux/sched.h +8 -1
index 1d74c3e..c99f5d8 100644
@@ -1296,8 +1296,12 @@ struct sched_dl_entity {
 	 * @dl_new tells if a new instance arrived. If so we must
 	 * start executing it with full runtime and reset its absolute
 	 * deadline;
+	 *
+	 * @dl_boosted tells if we are boosted due to DI. If so we are
+	 * outside bandwidth enforcement mechanism (but only until we
+	 * exit the critical section).
 	 */
-	int dl_throttled, dl_new;
+	int dl_throttled, dl_new, dl_boosted;
 
 	/*
 	 * Bandwidth enforcement timer. Each -deadline task has its
@@ -1536,6 +1540,8 @@ struct task_struct {
 	struct rb_node *pi_waiters_leftmost;
 	/* Deadlock detection and priority inheritance handling */
 	struct rt_mutex_waiter *pi_blocked_on;
+	/* Top pi_waiters task */
+	struct task_struct *pi_top_task;
 #endif
 
 #ifdef CONFIG_DEBUG_MUTEXES
@@ -2187,6 +2193,7 @@ extern unsigned int sysctl_sched_cfs_bandwidth_slice;
 #ifdef CONFIG_RT_MUTEXES
 extern int rt_mutex_getprio(struct task_struct *p);
 extern void rt_mutex_setprio(struct task_struct *p, int prio);
+extern struct task_struct *rt_mutex_get_top_task(struct task_struct *task);
 extern void rt_mutex_adjust_pi(struct task_struct *p);
 static inline bool tsk_is_pi_blocked(struct task_struct *tsk)
 {
kernel/fork.c +1 -0
index 12c531b..fe01cc2 100644
@@ -1114,6 +1114,7 @@ static void rt_mutex_init_task(struct task_struct *p)
 	p->pi_waiters = RB_ROOT;
 	p->pi_waiters_leftmost = NULL;
 	p->pi_blocked_on = NULL;
+	p->pi_top_task = NULL;
 #endif
 }
 
kernel/rtmutex.c +11 -2
index c9a71eb..7187c50 100644
@@ -199,6 +199,14 @@ int rt_mutex_getprio(struct task_struct *task)
 		   task->normal_prio);
 }
 
+struct task_struct *rt_mutex_get_top_task(struct task_struct *task)
+{
+	if (likely(!task_has_pi_waiters(task)))
+		return NULL;
+
+	return task_top_pi_waiter(task)->task;
+}
+
 /*
  * Adjust the priority of a task, after its pi_waiters got modified.
  *
@@ -208,7 +216,7 @@ static void __rt_mutex_adjust_prio(struct task_struct *task)
 {
 	int prio = rt_mutex_getprio(task);
 
-	if (task->prio != prio)
+	if (task->prio != prio || dl_prio(prio))
 		rt_mutex_setprio(task, prio);
 }
 
@@ -638,7 +646,8 @@ void rt_mutex_adjust_pi(struct task_struct *task)
 	raw_spin_lock_irqsave(&task->pi_lock, flags);
 
 	waiter = task->pi_blocked_on;
-	if (!waiter || waiter->task->prio == task->prio) {
+	if (!waiter || (waiter->task->prio == task->prio &&
+			!dl_prio(task->prio))) {
 		raw_spin_unlock_irqrestore(&task->pi_lock, flags);
 		return;
 	}
kernel/sched/core.c +29 -5
index e1f312a..b304610 100644
@@ -3838,7 +3838,7 @@ EXPORT_SYMBOL(sleep_on_timeout);
  */
 void rt_mutex_setprio(struct task_struct *p, int prio)
 {
-	int oldprio, on_rq, running;
+	int oldprio, on_rq, running, enqueue_flag = 0;
 	struct rq *rq;
 	const struct sched_class *prev_class;
 
@@ -3865,6 +3865,7 @@ void rt_mutex_setprio(struct task_struct *p, int prio)
 	}
 
 	trace_sched_pi_setprio(p, prio);
+	p->pi_top_task = rt_mutex_get_top_task(p);
 	oldprio = p->prio;
 	prev_class = p->sched_class;
 	on_rq = p->on_rq;
@@ -3874,19 +3875,42 @@ void rt_mutex_setprio(struct task_struct *p, int prio)
 	if (running)
 		p->sched_class->put_prev_task(rq, p);
 
-	if (dl_prio(prio))
+	/*
+	 * Boosting condition are:
+	 * 1. -rt task is running and holds mutex A
+	 *      --> -dl task blocks on mutex A
+	 *
+	 * 2. -dl task is running and holds mutex A
+	 *      --> -dl task blocks on mutex A and could preempt the
+	 *          running task
+	 */
+	if (dl_prio(prio)) {
+		if (!dl_prio(p->normal_prio) || (p->pi_top_task &&
+			dl_entity_preempt(&p->pi_top_task->dl, &p->dl))) {
+			p->dl.dl_boosted = 1;
+			p->dl.dl_throttled = 0;
+			enqueue_flag = ENQUEUE_REPLENISH;
+		} else
+			p->dl.dl_boosted = 0;
 		p->sched_class = &dl_sched_class;
-	else if (rt_prio(prio))
+	} else if (rt_prio(prio)) {
+		if (dl_prio(oldprio))
+			p->dl.dl_boosted = 0;
+		if (oldprio < prio)
+			enqueue_flag = ENQUEUE_HEAD;
 		p->sched_class = &rt_sched_class;
-	else
+	} else {
+		if (dl_prio(oldprio))
+			p->dl.dl_boosted = 0;
 		p->sched_class = &fair_sched_class;
+	}
 
 	p->prio = prio;
 
 	if (running)
 		p->sched_class->set_curr_task(rq);
 	if (on_rq)
-		enqueue_task(rq, p, oldprio < prio ? ENQUEUE_HEAD : 0);
+		enqueue_task(rq, p, enqueue_flag);
 
 	check_class_changed(rq, p, prev_class, oldprio);
 out_unlock:
kernel/sched/dl.c +53 -38

208 lines not on this page · raw message

kernel/sched/sched.h +14 -0
index 996a992..04ec6df 100644
@@ -67,6 +67,20 @@ static inline int task_has_dl_policy(struct task_struct *p)
 	return dl_policy(p->policy);
 }
 
+static inline int dl_time_before(u64 a, u64 b)
+{
+	return (s64)(a - b) < 0;
+}
+
+/*
+ * Tells if entity @a should preempt entity @b.
+ */
+static inline
+int dl_entity_preempt(struct sched_dl_entity *a, struct sched_dl_entity *b)
+{
+	return dl_time_before(a->deadline, b->deadline);
+}
+
 /*
  * This is the priority-queue data structure of the RT scheduling class:
  */
Signature · 1 line
1.7.9.5
author
#13 lore raw

PATCH 13/15 sched: add bandwidth management for sched_dl.

In reply to Juri Lelli

From: Dario Faggioli <[email protected]>

In order of -deadline scheduling to be effective and useful, it is important that some method of having the allocation of the available CPU bandwidth to tasks and task groups under control.
This is usually called "admission control" and if it is not performed at all, no guarantee can be given on the actual scheduling of the -deadline tasks.

Since when RT-throttling has been introduced each task group have a bandwidth associated to itself, calculated as a certain amount of runtime over a period. Moreover, to make it possible to manipulate such bandwidth, readable/writable controls have been added to both procfs (for system wide settings) and cgroupfs (for per-group settings).
Therefore, the same interface is being used for controlling the bandwidth distrubution to -deadline tasks and task groups, i.e., new controls but with similar names, equivalent meaning and with the same usage paradigm are added.

However, more discussion is needed in order to figure out how we want to manage SCHED_DEADLINE bandwidth at the task group level. Therefore, this patch adds a less sophisticated, but actually very sensible, mechanism to ensure that a certain utilization cap is not overcome per each root_domain (the single rq for !SMP configurations).

Another main difference between deadline bandwidth management and RT-throttling is that -deadline tasks have bandwidth on their own (while -rt ones doesn't!), and thus we don't need an higher level throttling mechanism to enforce the desired bandwidth.

This patch, therefore:
- adds system wide deadline bandwidth management by means of:
* /proc/sys/kernel/sched_dl_runtime_us,
* /proc/sys/kernel/sched_dl_period_us,
that determine (i.e., runtime / period) the total bandwidth available on each CPU of each root_domain for -deadline tasks;
- couples the RT and deadline bandwidth management, i.e., enforces that the sum of how much bandwidth is being devoted to -rt -deadline tasks to stay below 100%.

This means that, for a root_domain comprising M CPUs, -deadline tasks can be created until the sum of their bandwidths stay below:

M * (sched_dl_runtime_us / sched_dl_period_us)

It is also possible to disable this bandwidth management logic, and be thus free of oversubscribing the system up to any arbitrary level.

---
 include/linux/sched.h |    8 +
 kernel/sched/core.c   |  413 ++++++++++++++++++++++++++++++++++++++++++++++---
 kernel/sched/dl.c     |   45 +++++-
 kernel/sched/sched.h  |   67 +++++++-
 kernel/sysctl.c       |   14 ++
 5 files changed, 518 insertions(+), 29 deletions(-)
include/linux/sched.h +8 -0
index c99f5d8..a8bda36 100644
@@ -1276,6 +1276,7 @@ struct sched_dl_entity {
 	u64 dl_runtime;		/* maximum runtime for each instance	*/
 	u64 dl_deadline;	/* relative deadline of each instance	*/
 	u64 dl_period;		/* separation of two instances (period) */
+	u64 dl_bw;		/* dl_runtime / dl_deadline		*/
 
 	/*
 	 * Actual scheduling parameters. Initialized with the values above,
@@ -2168,6 +2169,13 @@ int sched_rt_handler(struct ctl_table *table, int write,
 		void __user *buffer, size_t *lenp,
 		loff_t *ppos);
 
+extern unsigned int sysctl_sched_dl_period;
+extern int sysctl_sched_dl_runtime;
+
+int sched_dl_handler(struct ctl_table *table, int write,
+		void __user *buffer, size_t *lenp,
+		loff_t *ppos);
+
 #ifdef CONFIG_SCHED_AUTOGROUP
 extern unsigned int sysctl_sched_autogroup_enabled;
 
kernel/sched/core.c +391 -22

530 lines not on this page · raw message

kernel/sched/dl.c +41 -4

87 lines not on this page · raw message

kernel/sched/sched.h +64 -3

118 lines not on this page · raw message

kernel/sysctl.c +14 -0
index 4ab1187..778140f 100644
@@ -352,6 +352,20 @@ static struct ctl_table kern_table[] = {
 		.mode		= 0644,
 		.proc_handler	= sched_rt_handler,
 	},
+	{
+		.procname	= "sched_dl_period_us",
+		.data		= &sysctl_sched_dl_period,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= sched_dl_handler,
+	},
+	{
+		.procname	= "sched_dl_runtime_us",
+		.data		= &sysctl_sched_dl_runtime,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= sched_dl_handler,
+	},
 #ifdef CONFIG_SCHED_AUTOGROUP
 	{
 		.procname	= "sched_autogroup_enabled",
Signature · 1 line
1.7.9.5
maintainer
#14 lore raw

Re: [PATCH 13/15] sched: add bandwidth management for sched_dl.

Quoted 5 lines · Juri Lelli

+/*
+ * Coupling of -dl and -rt bandwidth.
+ *
+ * Here we check, while setting the system wide bandwidth available
+ * for -dl tasks and groups, if the new values are consistent with

Quoted 8 lines

+ * the system settings for the bandwidth available to -rt entities.
+ *
+ * IOW, we want to enforce that
+ *
+ * rt_bandwidth + dl_bandwidth <= 100%
+ *
+ * is always true.
+ */

I was thinking we could do it the other way around, have have dl_bandwidth included in rt_bandwidth.

author
#15 lore raw

Re: [PATCH 13/15] sched: add bandwidth management for sched_dl.

Hi,

Quoted 15 lines · Peter Zijlstra
Quoted 5 lines · Juri Lelli

+/*
+ * Coupling of -dl and -rt bandwidth.
+ *
+ * Here we check, while setting the system wide bandwidth available
+ * for -dl tasks and groups, if the new values are consistent with

Quoted 8 lines

+ * the system settings for the bandwidth available to -rt entities.
+ *
+ * IOW, we want to enforce that
+ *
+ * rt_bandwidth + dl_bandwidth<= 100%
+ *
+ * is always true.
+ */

Quoted 3 lines

I was thinking we could do it the other way around, have have dl_bandwidth included in rt_bandwidth.

If I understand correctly, you are proposing to treat -dl tasks as a special case of "real-time" tasks. Then we could reserve some bw to "real-time" (rt_bandwidth cap) activities and give a piece of this bw to -dl tasks (what remains is for -rt tasks). This is in principle nice and useful, but I'm not quite sure that this is the right point to achieve this logical behavior.
I mean, -dl and -rt tasks are separately treated, so it is probably cleaner to manage their knobs separately. They have to coexist rather than be considered one a sub-case of the other. A better way to go for a common cap for them is probably the (long-term) hierarchical scheduling mechanism.

So, I would prefer to keep the interface as is for now, but I can also completely misunderstood your thoughts :-P.

Thanks and Regards,

- Juri

maintainer
#16 lore raw

Re: [PATCH 13/15] sched: add bandwidth management for sched_dl.

Quoted 34 lines · Juri Lelli

Hi,

Quoted 15 lines · Peter Zijlstra
Quoted 5 lines · Juri Lelli

+/*
+ * Coupling of -dl and -rt bandwidth.
+ *
+ * Here we check, while setting the system wide bandwidth available
+ * for -dl tasks and groups, if the new values are consistent with

Quoted 8 lines

+ * the system settings for the bandwidth available to -rt entities.
+ *
+ * IOW, we want to enforce that
+ *
+ * rt_bandwidth + dl_bandwidth<= 100%
+ *
+ * is always true.
+ */

Quoted 3 lines

I was thinking we could do it the other way around, have have dl_bandwidth included in rt_bandwidth.

If I understand correctly, you are proposing to treat -dl tasks as a special case of "real-time" tasks. Then we could reserve some bw to "real-time" (rt_bandwidth cap) activities and give a piece of this bw to -dl tasks (what remains is for -rt tasks). This is in principle nice and useful, but I'm not quite sure that this is the right point to achieve this logical behavior.
I mean, -dl and -rt tasks are separately treated, so it is probably cleaner to manage their knobs separately. They have to coexist rather than be considered one a sub-case of the other. A better way to go for a common cap for them is probably the (long-term) hierarchical scheduling mechanism.

Quoted 2 lines

So, I would prefer to keep the interface as is for now, but I can also completely misunderstood your thoughts :-P.

The thing is, keeping it separate makes for an impossible configuration scenario. Esp. once we enable !root usage. The proposed 5% is very limiting and regular users won't have sufficient privilege to change it.

Also lowering FIFO/RR by default isn't a real option since people expect that to get all time already (however silly that expectation is).

author
#17 lore raw

Re: [PATCH 13/15] sched: add bandwidth management for sched_dl.

Quoted 35 lines · Peter Zijlstra
Quoted 34 lines · Juri Lelli

Hi,

Quoted 15 lines · Peter Zijlstra
Quoted 5 lines · Juri Lelli

+/*
+ * Coupling of -dl and -rt bandwidth.
+ *
+ * Here we check, while setting the system wide bandwidth available
+ * for -dl tasks and groups, if the new values are consistent with

Quoted 8 lines

+ * the system settings for the bandwidth available to -rt entities.
+ *
+ * IOW, we want to enforce that
+ *
+ * rt_bandwidth + dl_bandwidth<= 100%
+ *
+ * is always true.
+ */

Quoted 3 lines

I was thinking we could do it the other way around, have have dl_bandwidth included in rt_bandwidth.

If I understand correctly, you are proposing to treat -dl tasks as a special case of "real-time" tasks. Then we could reserve some bw to "real-time" (rt_bandwidth cap) activities and give a piece of this bw to -dl tasks (what remains is for -rt tasks). This is in principle nice and useful, but I'm not quite sure that this is the right point to achieve this logical behavior.
I mean, -dl and -rt tasks are separately treated, so it is probably cleaner to manage their knobs separately. They have to coexist rather than be considered one a sub-case of the other. A better way to go for a common cap for them is probably the (long-term) hierarchical scheduling mechanism.

Quoted 7 lines
Quoted 2 lines

So, I would prefer to keep the interface as is for now, but I can also completely misunderstood your thoughts :-P.

The thing is, keeping it separate makes for an impossible configuration scenario. Esp. once we enable !root usage. The proposed 5% is very limiting and regular users won't have sufficient privilege to change it.

Ok, now I understand your point better, and I agree that 5% is hardly usable for !root users. However, I also think this is probably more a system admin problem. I mean, a sys admin that wants his users to play with -deadline scheduling should have thought how to properly set up his system, and the fact that something must be configured by hand to give users a usable system is generally not a so bad idea.

Quoted 3 lines

Also lowering FIFO/RR by default isn't a real option since people expect that to get all time already (however silly that expectation is).

I agree. Don't want to spoil users expectations :-).

Thanks and regards,

- Juri

maintainer
#18 lore raw

Re: [PATCH 13/15] sched: add bandwidth management for sched_dl.

Quoted 11 lines · Juri Lelli
Quoted 4 lines

The thing is, keeping it separate makes for an impossible configuration scenario. Esp. once we enable !root usage. The proposed 5% is very limiting and regular users won't have sufficient privilege to change it.

Ok, now I understand your point better, and I agree that 5% is hardly usable for !root users. However, I also think this is probably more a system admin problem. I mean, a sys admin that wants his users to play with -deadline scheduling should have thought how to properly set up his system, and the fact that something must be configured by hand to give users a usable system is generally not a so bad idea.

Yes, but lets not make the life of the unsuspecting admin harder than we absolutely have to.

Furthermore we should strive to make DL as useful as possible out-of-the-box.

One way to do this is to have rt and dl bandwidth constraints independent and put a 3rd combined limit in place. But what is the benefit of that over a single combined limit?

author
#19 lore raw

Re: [PATCH 13/15] sched: add bandwidth management for sched_dl.

Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Juri Lelli
Quoted 4 lines

The thing is, keeping it separate makes for an impossible configuration scenario. Esp. once we enable !root usage. The proposed 5% is very limiting and regular users won't have sufficient privilege to change it.

Ok, now I understand your point better, and I agree that 5% is hardly usable for !root users. However, I also think this is probably more a system admin problem. I mean, a sys admin that wants his users to play with -deadline scheduling should have thought how to properly set up his system, and the fact that something must be configured by hand to give users a usable system is generally not a so bad idea.

Quoted 6 lines

Yes, but lets not make the life of the unsuspecting admin harder than we absolutely have to.

Furthermore we should strive to make DL as useful as possible out-of-the-box.

Ok, I agree on this.

Quoted 3 lines

One way to do this is to have rt and dl bandwidth constraints independent and put a 3rd combined limit in place. But what is the benefit of that over a single combined limit?

In the end, a single limit is probably better. So, I'll leave rt_bandwidth at its default value and make dl_bandwidth a sort of sub-quota of it. To give the user an out-of-the-box usable system, I could raise dl_bandwith to, let's say, 40% (of 95%). Thus, in the default scenario, the user will be allowed to create _rt tasks up to 57% and _dl tasks up to 38% of the system capacity, sounds reasonable?

Thanks and regards,

- Juri

author
#20 lore raw

PATCH 14/15 sched: speed up -dl pushes with a push-heap.

In reply to Juri Lelli

Data from tests confirmed that the original active load balancing logic didn't scale neither in the number of CPU nor in the number of tasks (as sched_rt does).

Here we provide a global data structure to keep track of deadlines of the running tasks in the system. The structure is composed by a bitmask showing the free CPUs and a max-heap, needed when the system is heavily loaded.

The implementation and concurrent access scheme are kept simple by design. However, our measurements show that we can compete with sched_rt on large multi-CPUs machines [1].

Only the push path is addressed, the extension to use this structure also for pull decisions is straightforward. However, we are currently evaluating different (in order to decrease/avoid contention) data structures to solve possibly both problems. We are also going to re-run tests considering recent changes inside cpupri [2].

[1] http://retis.sssup.it/~jlelli/papers/Ospert11Lelli.pdf
[2] http://www.spinics.net/lists/linux-rt-users/msg06778.html

---
 kernel/sched/Makefile |    2 +-
 kernel/sched/core.c   |    3 +
 kernel/sched/cpudl.c  |  208 +++++++++++++++++++++++++++++++++++++++++++++++++
 kernel/sched/cpudl.h  |   33 ++++++++
 kernel/sched/dl.c     |   52 ++++---------
 kernel/sched/sched.h  |    2 +
 6 files changed, 260 insertions(+), 40 deletions(-)
 create mode 100644 kernel/sched/cpudl.c
 create mode 100644 kernel/sched/cpudl.h
kernel/sched/Makefile +1 -1
index 73aa1a3..fc69041 100644
@@ -12,7 +12,7 @@ CFLAGS_core.o := $(PROFILING) -fno-omit-frame-pointer
 endif
 
 obj-y += core.o clock.o idle_task.o fair.o rt.o dl.o stop_task.o
-obj-$(CONFIG_SMP) += cpupri.o
+obj-$(CONFIG_SMP) += cpupri.o cpudl.o
 obj-$(CONFIG_SCHED_AUTOGROUP) += auto_group.o
 obj-$(CONFIG_SCHEDSTATS) += stats.o
 obj-$(CONFIG_SCHED_DEBUG) += debug.o
kernel/sched/core.c +3 -0
index 23cfcc3..85451e9 100644
@@ -6106,6 +6106,7 @@ static void free_rootdomain(struct rcu_head *rcu)
 	struct root_domain *rd = container_of(rcu, struct root_domain, rcu);
 
 	cpupri_cleanup(&rd->cpupri);
+	cpudl_cleanup(&rd->cpudl);
 	free_cpumask_var(rd->dlo_mask);
 	free_cpumask_var(rd->rto_mask);
 	free_cpumask_var(rd->online);
@@ -6164,6 +6165,8 @@ static int init_rootdomain(struct root_domain *rd)
 		goto free_dlo_mask;
 
 	init_dl_bw(&rd->dl_bw);
+	if (cpudl_init(&rd->cpudl) != 0)
+		goto free_dlo_mask;
 
 	if (cpupri_init(&rd->cpupri) != 0)
 		goto free_rto_mask;
kernel/sched/cpudl.c new file +208 -0

208 lines not on this page · raw message

kernel/sched/cpudl.h new file +33 -0
new file mode 100644
index 0000000..7f93e64
@@ -0,0 +1,33 @@
+#ifndef _LINUX_CPUDL_H
+#define _LINUX_CPUDL_H
+
+#include <linux/sched.h>
+
+#define IDX_INVALID     -1
+
+struct array_item {
+	u64 dl;
+	int cpu;
+};
+
+struct cpudl {
+	raw_spinlock_t lock;
+	int size;
+	int cpu_to_idx[NR_CPUS];
+	struct array_item elements[NR_CPUS];
+	cpumask_var_t free_cpus;
+};
+
+
+#ifdef CONFIG_SMP
+int cpudl_find(struct cpudl *cp, struct cpumask *dlo_mask,
+		struct task_struct *p, struct cpumask *later_mask);
+void cpudl_set(struct cpudl *cp, int cpu, u64 dl, int is_valid);
+int cpudl_init(struct cpudl *cp);
+void cpudl_cleanup(struct cpudl *cp);
+#else
+#define cpudl_set(cp, cpu, dl) do { } while (0)
+#define cpudl_init() do { } while (0)
+#endif /* CONFIG_SMP */
+
+#endif /* _LINUX_CPUDL_H */
kernel/sched/dl.c +13 -39

112 lines not on this page · raw message

kernel/sched/sched.h +2 -0
index d32b7b6..6e32ddd 100644
@@ -5,6 +5,7 @@
 #include <linux/stop_machine.h>
 
 #include "cpupri.h"
+#include "cpudl.h"
 
 extern __read_mostly int scheduler_running;
 
@@ -444,6 +445,7 @@ struct root_domain {
 	cpumask_var_t dlo_mask;
 	atomic_t dlo_count;
 	struct dl_bw dl_bw;
+	struct cpudl cpudl;
 
 	/*
 	 * The "RT overload" flag: it gets set if a CPU has more than
Signature · 1 line
1.7.9.5
author
#21 lore raw

PATCH 15/15 sched: add sched_dl documentation.

In reply to Juri Lelli

From: Dario Faggioli <[email protected]>

Add in Documentation/scheduler/ some hints about the design choices, the usage and the future possible developments of the sched_dl scheduling class and of the SCHED_DEADLINE policy.

---
 Documentation/scheduler/sched-deadline.txt |  164 ++++++++++++++++++++++++++++
 kernel/sched/dl.c                          |    3 +-
 2 files changed, 166 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/scheduler/sched-deadline.txt
Documentation/scheduler/sched-deadline.txt new file +164 -0
new file mode 100644
index 0000000..d4dcfc7
@@ -0,0 +1,164 @@
+			Deadline Task and Group Scheduling
+			----------------------------------
+
+CONTENTS
+========
+
+0. WARNING
+1. Overview
+2. Task scheduling
+2. The Interface
+3. Bandwidth management
+  3.1 System-wide settings
+  2.2 Task interface
+  2.4 Default behavior
+3. Future plans
+
+
+0. WARNING
+==========
+
+ Fiddling with these settings can result in an unpredictable or even unstable
+ system behavior. As for -rt (group) scheduling, it is assumed that root users
+ know what they're doing.
+
+
+1. Overview
+===========
+
+ The SCHED_DEADLINE policy contained inside the sched_dl scheduling class is
+ basically an implementation of the Earliest Deadline First (EDF) scheduling
+ algorithm, augmented with a mechanism (called Constant Bandwidth Server, CBS)
+ that makes it possible to isolate the behavior of tasks between each other.
+
+
+2. Task scheduling
+==================
+
+ The typical -deadline task is composed of a computation phase (instance)
+ which is activated on a periodic or sporadic fashion. The expected (maximum)
+ duration of such computation is called the task's runtime; the time interval
+ by which each instance needs to be completed is called the task's relative
+ deadline. The task's absolute deadline is dynamically calculated as the
+ time instant a task (or, more properly) activates plus the relative
+ deadline.
+
+ The EDF[1] algorithm selects the task with the smallest absolute deadline as
+ the one to be executed first, while the CBS[2,3] ensures that each task runs
+ for at most its runtime every period, avoiding any interference between
+ different tasks (bandwidth isolation).
+ Thanks to this feature, also tasks that do not strictly comply with the
+ computational model described above can effectively use the new policy.
+ IOW, there are no limitations on what kind of task can exploit this new
+ scheduling discipline, even if it must be said that it is particularly
+ suited for periodic or sporadic tasks that need guarantees on their
+ timing behavior, e.g., multimedia, streaming, control applications, etc.
+
+ References:
+  1 - C. L. Liu and J. W. Layland. Scheduling algorithms for multiprogram-
+      ming in a hard-real-time environment. Journal of the Association for
+      Computing Machinery, 20(1), 1973.
+  2 - L. Abeni , G. Buttazzo. Integrating Multimedia Applications in Hard
+      Real-Time Systems. Proceedings of the 19th IEEE Real-time Systems
+      Symposium, 1998. http://retis.sssup.it/~giorgio/paps/1998/rtss98-cbs.pdf
+  3 - L. Abeni. Server Mechanisms for Multimedia Applications. ReTiS Lab
+      Technical Report. http://xoomer.virgilio.it/lucabe72/pubs/tr-98-01.ps
+
+3. Bandwidth management
+=======================
+
+ In order for the -deadline scheduling to be effective and useful, it is
+ important to have some method to keep the allocation of the available CPU
+ bandwidth to the tasks under control.
+ This is usually called "admission control" and if it is not performed at all,
+ no guarantee can be given on the actual scheduling of the -deadline tasks.
+
+ Since when RT-throttling has been introduced each task group has a bandwidth
+ associated, calculated as a certain amount of runtime over a period.
+ Moreover, to make it possible to manipulate such bandwidth, readable/writable
+ controls have been added to both procfs (for system wide settings) and cgroupfs
+ (for per-group settings).
+ Therefore, the same interface is being used for controlling the bandwidth
+ distrubution to -deadline tasks and task groups, i.e., new controls but with
+ similar names, equivalent meaning and with the same usage paradigm are added.
+
+ However, more discussion is needed in order to figure out how we want to manage
+ SCHED_DEADLINE bandwidth at the task group level. Therefore, SCHED_DEADLINE
+ uses (for now) a less sophisticated, but actually very sensible, mechanism to
+ ensure that a certain utilization cap is not overcome per each root_domain.
+
+ Another main difference between deadline bandwidth management and RT-throttling
+ is that -deadline tasks have bandwidth on their own (while -rt ones don't!),
+ and thus we don't need an higher level throttling mechanism to enforce the
+ desired bandwidth.
+
+3.1 System wide settings
+------------------------
+
+The system wide settings are configured under the /proc virtual file system:
+
+ The per-group controls that are added to the cgroupfs virtual file system are:
+  * /proc/sys/kernel/sched_dl_runtime_us,
+  * /proc/sys/kernel/sched_dl_period_us,
+
+ They accept (if written) and provides (if read) the new runtime and period,
+ respectively, for each CPU in each root_domain.
+
+ This means that, for a root_domain comprising M CPUs, -deadline tasks
+ can be created until the sum of their bandwidths stay below:
+
+   M * (sched_dl_runtime_us / sched_dl_period_us)
+
+ It is also possible to disable this bandwidth management logic, and
+ be thus free of oversubscribing the system up to any arbitrary level.
+ This is done by writing -1 in /proc/sys/kernel/sched_dl_runtime_us.
+
+
+2.2 Task interface
+------------------
+
+ Specifying a periodic/sporadic task that executes for a given amount of
+ runtime at each instance, and that is scheduled according to the urgency of
+ its own timing constraints needs, in general, a way of declaring:
+  - a (maximum/typical) instance execution time,
+  - a minimum interval between consecutive instances,
+  - a time constraint by which each instance must be completed.
+
+ Therefore:
+  * a new struct sched_param2, containing all the necessary fields is
+    provided;
+  * the new scheduling related syscalls that manipulate it, i.e.,
+    sched_setscheduler2(), sched_setparam2() and sched_getparam2()
+    are implemented.
+
+
+2.4 Default behavior
+---------------------
+
+The default values for SCHED_DEADLINE bandwidth is to have dl_runtime and
+dl_period equal to 500000 and 1000000, respectively. This means -deadline
+tasks can use at most 5%, multiplied by the number of CPUs that compose the
+root_domain, for each root_domain.
+
+When a -deadline task fork a child, its dl_runtime is set to 0, which means
+someone must call sched_setscheduler2() on it, or it won't even start.
+
+
+3. Future plans
+===============
+
+Still missing:
+
+ - refinements to deadline inheritance, especially regarding the possibility
+   of retaining bandwidth isolation among non-interacting tasks. This is
+   being studied from both theoretical and practical point of views, and
+   hopefully we should be able to produce some demonstrative code soon;
+ - (c)group based bandwidth management, and maybe scheduling;
+ - access control for non-root users (and related security concerns to
+   address), which is the best way to allow unprivileged use of the mechanisms
+   and how to prevent non-root users "cheat" the system?
+
+As already discussed, we are planning also to merge this work with the EDF
+throttling patches [https://lkml.org/lkml/2010/2/23/239] but we still are in
+the preliminary phases of the merge and we really seek feedback that would help us
+decide on the direction it should take.
kernel/sched/dl.c +2 -1
index 941a83a..3e28ed3 100644
@@ -346,7 +346,8 @@ static void replenish_dl_entity(struct sched_dl_entity *dl_se,
  * disrupting the schedulability of the system. Otherwise, we should
  * refill the runtime and set the deadline a period in the future,
  * because keeping the current (absolute) deadline of the task would
- * result in breaking guarantees promised to other tasks.
+ * result in breaking guarantees promised to other tasks (refer to
+ * Documentation/scheduler/sched-deadline.txt for more informations).
  *
  * This function returns true if:
  *
Signature · 1 line
1.7.9.5
maintainer
#22 lore raw
In reply to Juri Lelli
Quoted 2 lines · Juri Lelli

Still missing/incomplete:
- (c)group based bandwidth management, and maybe scheduling;

The b/w stuff for cgroups shouldn't be particularly hard, right? It shouldn't be more than ensuring the utilization sum of all children stays below the set value.

Or am I missing something?

Quoted 1 line

- bandwidth inheritance (to replace deadline/priority inheritance);

Yes please, but this is somewhat longer term, I think the band-aids in place are sufficient to allow us to move fwd.

Quoted 2 lines

- access control for non-root users (and related security concerns to address).

Right, so could you detail what all is needed to allow regular users to create dl tasks?

Aside from this, Ingo, Thomas, Steven do you agree in merging these patches as a suitable base to continue work from?

#23 lore raw
Quoted 15 lines · Peter Zijlstra
Quoted 2 lines · Juri Lelli

Still missing/incomplete:
- (c)group based bandwidth management, and maybe scheduling;

The b/w stuff for cgroups shouldn't be particularly hard, right? It shouldn't be more than ensuring the utilization sum of all children stays below the set value.

Or am I missing something?

Quoted 1 line

- bandwidth inheritance (to replace deadline/priority inheritance);

Yes please, but this is somewhat longer term, I think the band-aids in place are sufficient to allow us to move fwd.

Quoted 8 lines
Quoted 2 lines

- access control for non-root users (and related security concerns to address).

Right, so could you detail what all is needed to allow regular users to create dl tasks?

Aside from this, Ingo, Thomas, Steven do you agree in merging these patches as a suitable base to continue work from?

I haven't looked in detail, but I didn't spot horrible things either. And yes, we should start with that set and improve from there.

Thanks,

tglx

author
#24 lore raw
In reply to Peter Zijlstra

Hi,

Quoted 10 lines · Peter Zijlstra
Quoted 2 lines · Juri Lelli

Still missing/incomplete:
- (c)group based bandwidth management, and maybe scheduling;

The b/w stuff for cgroups shouldn't be particularly hard, right? It shouldn't be more than ensuring the utilization sum of all children stays below the set value.

Or am I missing something?

Yes, the sum of the bandwidths of entities (tasks and groups) belonging to a group stays below its own set bandwidth. Actually, I quoted this sentence from code that was included in previous versions (removed since v3). I tried to build historical reasons why it was removed, but I failed :-). Anyway, shouldn't be too hard to adapt that code to the new version and see if it will raise any new concern.

Quoted 5 lines
Quoted 1 line

- bandwidth inheritance (to replace deadline/priority inheritance);

Yes please, but this is somewhat longer term, I think the band-aids in place are sufficient to allow us to move fwd.

Yes, but I fear it could affect next point. Anyway, I agree that this is something that I would consider long-term.

Quoted 6 lines
Quoted 2 lines

- access control for non-root users (and related security concerns to address).

Right, so could you detail what all is needed to allow regular users to create dl tasks?

Ok, I'll try to summarize what I think it would be a _minimal_ set of requirements:

o define deadline(period) and runtime utilization caps; as it was in v3, RLIMIT_DLDLINE would be the minimum value a user task can use as its own deadline, while (as you pointed out) RLIMIT_DLRTIME would be a per user utilization cap, but it would also be tracked in user_struct to enforce a max utilization cap per user.

o At server parameters change/destruction some mechanism is needed to prevent possible attacks such as (ref. Tommaso's paper):

- an app creates a task with an associated server;
- when the runtime (budget) is about to be exhausted, the app destroys the server, returning the task to the default sched policy;
- then, the app immediately creates a new server for the task, and keeps repeating the loop over and over.

A possible solution is that, whenever a server is destroyed, the system doesn't completely forget about its existence, but it must continue to consider its utilization as not available until the next server period expired.

o The only "big problem" I fear is related to the current deadline inheritance mechanism. As Steven pointed out during the last review, a task could force more bandwidth than was allowed if:

- it takes a futex;
- it is boosted while holding it;
- never releases the futex;

and this is sadly achievable since, if boosted, a task inherits top waiter parameters and the enforcement mechanism is temporarily disabled for it.

Thanks and regards,

- Juri

maintainer
#25 lore raw
Quoted 11 lines · Juri Lelli

o The only "big problem" I fear is related to the current deadline inheritance mechanism. As Steven pointed out during the last review, a task could force more bandwidth than was allowed if:

- it takes a futex;
- it is boosted while holding it;
- never releases the futex;

and this is sadly achievable since, if boosted, a task inherits top waiter parameters and the enforcement mechanism is temporarily disabled for it.

/me goes read patch 12 in detail..

Wouldn't it be possible to enforce pi_se (aka. pi_task)'s bandwidth in the dl-boosts-dl case?

Since afaict that's the only case we need to worry about. If you can spawn FIFO/RR tasks you already loose bandwidth caps etc..