diff options
| author | Dario Faggioli <raistlin@linux.it> | 2013-11-07 08:43:45 -0500 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2014-01-13 07:46:42 -0500 |
| commit | 332ac17ef5bfcff4766dfdfd3b4cdf10b8f8f155 (patch) | |
| tree | 84c6663542da4310c5c555afaac88ac9b696fe4b | |
| parent | 2d3d891d3344159d5b452a645e355bbe29591e8b (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.h | 1 | ||||
| -rw-r--r-- | include/linux/sched/sysctl.h | 13 | ||||
| -rw-r--r-- | kernel/sched/core.c | 441 | ||||
| -rw-r--r-- | kernel/sched/deadline.c | 46 | ||||
| -rw-r--r-- | kernel/sched/sched.h | 76 | ||||
| -rw-r--r-- | kernel/sysctl.c | 14 |
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) | |||
| 81 | extern unsigned int sysctl_sched_rt_period; | 81 | extern unsigned int sysctl_sched_rt_period; |
| 82 | extern int sysctl_sched_rt_runtime; | 82 | extern 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 | */ | ||
| 90 | extern unsigned int sysctl_sched_dl_period; | ||
| 91 | extern int sysctl_sched_dl_runtime; | ||
| 92 | |||
| 84 | #ifdef CONFIG_CFS_BANDWIDTH | 93 | #ifdef CONFIG_CFS_BANDWIDTH |
| 85 | extern unsigned int sysctl_sched_cfs_bandwidth_slice; | 94 | extern 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 | ||
| 111 | int 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 | */ |
| 297 | int sysctl_sched_rt_runtime = 950000; | 297 | int 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 | */ | ||
| 305 | unsigned int sysctl_sched_dl_period = 1000000; | ||
| 306 | int 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 | ||
| 1868 | unsigned 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 | ||
| 1885 | inline struct dl_bw *dl_bw_of(int i) | ||
| 1886 | { | ||
| 1887 | return &cpu_rq(i)->rd->dl_bw; | ||
| 1888 | } | ||
| 1889 | |||
| 1890 | static inline int __dl_span_weight(struct rq *rq) | ||
| 1891 | { | ||
| 1892 | return cpumask_weight(rq->rd->span); | ||
| 1893 | } | ||
| 1894 | #else | ||
| 1895 | inline struct dl_bw *dl_bw_of(int i) | ||
| 1896 | { | ||
| 1897 | return &cpu_rq(i)->dl.dl_bw; | ||
| 1898 | } | ||
| 1899 | |||
| 1900 | static inline int __dl_span_weight(struct rq *rq) | ||
| 1901 | { | ||
| 1902 | return 1; | ||
| 1903 | } | ||
| 1904 | #endif | ||
| 1905 | |||
| 1906 | static inline | ||
| 1907 | void __dl_clear(struct dl_bw *dl_b, u64 tsk_bw) | ||
| 1908 | { | ||
| 1909 | dl_b->total_bw -= tsk_bw; | ||
| 1910 | } | ||
| 1911 | |||
| 1912 | static inline | ||
| 1913 | void __dl_add(struct dl_bw *dl_b, u64 tsk_bw) | ||
| 1914 | { | ||
| 1915 | dl_b->total_bw += tsk_bw; | ||
| 1916 | } | ||
| 1917 | |||
| 1918 | static inline | ||
| 1919 | bool __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 | */ | ||
| 1933 | static 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 | |||
| 1971 | extern 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 | |||
