aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDario Faggioli <raistlin@linux.it>2013-11-07 08:43:45 -0500
committerIngo Molnar <mingo@kernel.org>2014-01-13 07:46:42 -0500
commit332ac17ef5bfcff4766dfdfd3b4cdf10b8f8f155 (patch)
tree84c6663542da4310c5c555afaac88ac9b696fe4b
parent2d3d891d3344159d5b452a645e355bbe29591e8b (diff)
sched/deadline: Add bandwidth management for SCHED_DEADLINE tasks
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. Signed-off-by: Dario Faggioli <raistlin@linux.it> Signed-off-by: Juri Lelli <juri.lelli@gmail.com> Signed-off-by: Peter Zijlstra <peterz@infradead.org> Link: http://lkml.kernel.org/r/1383831828-15501-12-git-send-email-juri.lelli@gmail.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--include/linux/sched.h1
-rw-r--r--include/linux/sched/sysctl.h13
-rw-r--r--kernel/sched/core.c441
-rw-r--r--kernel/sched/deadline.c46
-rw-r--r--kernel/sched/sched.h76
-rw-r--r--kernel/sysctl.c14
6 files changed, 555 insertions, 36 deletions
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 13c53a99920f..a196cb7fc6f2 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1104,6 +1104,7 @@ struct sched_dl_entity {
1104 u64 dl_runtime; /* maximum runtime for each instance */ 1104 u64 dl_runtime; /* maximum runtime for each instance */
1105 u64 dl_deadline; /* relative deadline of each instance */ 1105 u64 dl_deadline; /* relative deadline of each instance */
1106 u64 dl_period; /* separation of two instances (period) */ 1106 u64 dl_period; /* separation of two instances (period) */
1107 u64 dl_bw; /* dl_runtime / dl_deadline */
1107 1108
1108 /* 1109 /*
1109 * Actual scheduling parameters. Initialized with the values above, 1110 * Actual scheduling parameters. Initialized with the values above,
diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h
index 31e0193cb0c5..8070a83dbedc 100644
--- a/include/linux/sched/sysctl.h
+++ b/include/linux/sched/sysctl.h
@@ -81,6 +81,15 @@ static inline unsigned int get_sysctl_timer_migration(void)
81extern unsigned int sysctl_sched_rt_period; 81extern unsigned int sysctl_sched_rt_period;
82extern int sysctl_sched_rt_runtime; 82extern int sysctl_sched_rt_runtime;
83 83
84/*
85 * control SCHED_DEADLINE reservations:
86 *
87 * /proc/sys/kernel/sched_dl_period_us
88 * /proc/sys/kernel/sched_dl_runtime_us
89 */
90extern unsigned int sysctl_sched_dl_period;
91extern int sysctl_sched_dl_runtime;
92
84#ifdef CONFIG_CFS_BANDWIDTH 93#ifdef CONFIG_CFS_BANDWIDTH
85extern unsigned int sysctl_sched_cfs_bandwidth_slice; 94extern unsigned int sysctl_sched_cfs_bandwidth_slice;
86#endif 95#endif
@@ -99,4 +108,8 @@ extern int sched_rt_handler(struct ctl_table *table, int write,
99 void __user *buffer, size_t *lenp, 108 void __user *buffer, size_t *lenp,
100 loff_t *ppos); 109 loff_t *ppos);
101 110
111int sched_dl_handler(struct ctl_table *table, int write,
112 void __user *buffer, size_t *lenp,
113 loff_t *ppos);
114
102#endif /* _SCHED_SYSCTL_H */ 115#endif /* _SCHED_SYSCTL_H */
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 599ee3b11b44..c7c68e6b5c51 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -296,6 +296,15 @@ __read_mostly int scheduler_running;
296 */ 296 */
297int sysctl_sched_rt_runtime = 950000; 297int sysctl_sched_rt_runtime = 950000;
298 298
299/*
300 * Maximum bandwidth available for all -deadline tasks and groups
301 * (if group scheduling is configured) on each CPU.
302 *
303 * default: 5%
304 */
305unsigned int sysctl_sched_dl_period = 1000000;
306int sysctl_sched_dl_runtime = 50000;
307
299 308
300 309
301/* 310/*
@@ -1856,6 +1865,111 @@ int sched_fork(unsigned long clone_flags, struct task_struct *p)
1856 return 0; 1865 return 0;
1857} 1866}
1858 1867
1868unsigned long to_ratio(u64 period, u64 runtime)
1869{
1870 if (runtime == RUNTIME_INF)
1871 return 1ULL << 20;
1872
1873 /*
1874 * Doing this here saves a lot of checks in all
1875 * the calling paths, and returning zero seems
1876 * safe for them anyway.
1877 */
1878 if (period == 0)
1879 return 0;
1880
1881 return div64_u64(runtime << 20, period);
1882}
1883
1884#ifdef CONFIG_SMP
1885inline struct dl_bw *dl_bw_of(int i)
1886{
1887 return &cpu_rq(i)->rd->dl_bw;
1888}
1889
1890static inline int __dl_span_weight(struct rq *rq)
1891{
1892 return cpumask_weight(rq->rd->span);
1893}
1894#else
1895inline struct dl_bw *dl_bw_of(int i)
1896{
1897 return &cpu_rq(i)->dl.dl_bw;
1898}
1899
1900static inline int __dl_span_weight(struct rq *rq)
1901{
1902 return 1;
1903}
1904#endif
1905
1906static inline
1907void __dl_clear(struct dl_bw *dl_b, u64 tsk_bw)
1908{
1909 dl_b->total_bw -= tsk_bw;
1910}
1911
1912static inline
1913void __dl_add(struct dl_bw *dl_b, u64 tsk_bw)
1914{
1915 dl_b->total_bw += tsk_bw;
1916}
1917
1918static inline
1919bool __dl_overflow(struct dl_bw *dl_b, int cpus, u64 old_bw, u64 new_bw)
1920{
1921 return dl_b->bw != -1 &&
1922 dl_b->bw * cpus < dl_b->total_bw - old_bw + new_bw;
1923}
1924
1925/*
1926 * We must be sure that accepting a new task (or allowing changing the
1927 * parameters of an existing one) is consistent with the bandwidth
1928 * constraints. If yes, this function also accordingly updates the currently
1929 * allocated bandwidth to reflect the new situation.
1930 *
1931 * This function is called while holding p's rq->lock.
1932 */
1933static int dl_overflow(struct task_struct *p, int policy,
1934 const struct sched_attr *attr)
1935{
1936
1937 struct dl_bw *dl_b = dl_bw_of(task_cpu(p));
1938 u64 period = attr->sched_period;
1939 u64 runtime = attr->sched_runtime;
1940 u64 new_bw = dl_policy(policy) ? to_ratio(period, runtime) : 0;
1941 int cpus = __dl_span_weight(task_rq(p));
1942 int err = -1;
1943
1944 if (new_bw == p->dl.dl_bw)
1945 return 0;
1946
1947 /*
1948 * Either if a task, enters, leave, or stays -deadline but changes
1949 * its parameters, we may need to update accordingly the total
1950 * allocated bandwidth of the container.
1951 */
1952 raw_spin_lock(&dl_b->lock);
1953 if (dl_policy(policy) && !task_has_dl_policy(p) &&
1954 !__dl_overflow(dl_b, cpus, 0, new_bw)) {
1955 __dl_add(dl_b, new_bw);
1956 err = 0;
1957 } else if (dl_policy(policy) && task_has_dl_policy(p) &&
1958 !__dl_overflow(dl_b, cpus, p->dl.dl_bw, new_bw)) {
1959 __dl_clear(dl_b, p->dl.dl_bw);
1960 __dl_add(dl_b, new_bw);
1961 err = 0;
1962 } else if (!dl_policy(policy) && task_has_dl_policy(p)) {
1963 __dl_clear(dl_b, p->dl.dl_bw);
1964 err = 0;
1965 }
1966 raw_spin_unlock(&dl_b->lock);
1967
1968 return err;
1969}
1970
1971extern void init_dl_bw(struct dl_bw *dl_b);
1972
1859/* 1973/*
1860 * wake_up_new_task - wake up a newly created task for the first time. 1974 * wake_up_new_task - wake up a newly created task for the first time.
1861 * 1975 *
@@ -3053,6 +3167,7 @@ __setparam_dl(struct task_struct *p, const struct sched_attr *attr)
3053 dl_se->dl_deadline = attr->sched_deadline; 3167 dl_se->dl_deadline = attr->sched_deadline;
3054 dl_se->dl_period = attr->sched_period ?: dl_se->dl_deadline; 3168 dl_se->dl_period = attr->sched_period ?: dl_se->dl_deadline;
3055 dl_se->flags = attr->sched_flags; 3169 dl_se->flags = attr->sched_flags;
3170 dl_se->dl_bw = to_ratio(dl_se->dl_period, dl_se->dl_runtime);
3056 dl_se->dl_throttled = 0; 3171 dl_se->dl_throttled = 0;
3057