]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
sched/deadline: Create DL BW alloc, free & check overflow interface
authorDietmar Eggemann <dietmar.eggemann@arm.com>
Sun, 20 Aug 2023 15:24:16 +0000 (16:24 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 30 Aug 2023 14:11:11 +0000 (16:11 +0200)
commit 85989106feb734437e2d598b639991b9185a43a6 upstream.

While moving a set of tasks between exclusive cpusets,
cpuset_can_attach() -> task_can_attach() calls dl_cpu_busy(..., p) for
DL BW overflow checking and per-task DL BW allocation on the destination
root_domain for the DL tasks in this set.

This approach has the issue of not freeing already allocated DL BW in
the following error cases:

(1) The set of tasks includes multiple DL tasks and DL BW overflow
    checking fails for one of the subsequent DL tasks.

(2) Another controller next to the cpuset controller which is attached
    to the same cgroup fails in its can_attach().

To address this problem rework dl_cpu_busy():

(1) Split it into dl_bw_check_overflow() & dl_bw_alloc() and add a
    dedicated dl_bw_free().

(2) dl_bw_alloc() & dl_bw_free() take a `u64 dl_bw` parameter instead of
    a `struct task_struct *p` used in dl_cpu_busy(). This allows to
    allocate DL BW for a set of tasks too rather than only for a single
    task.

Signed-off-by: Dietmar Eggemann <dietmar.eggemann@arm.com>
Signed-off-by: Juri Lelli <juri.lelli@redhat.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Qais Yousef (Google) <qyousef@layalina.io>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/linux/sched.h
kernel/sched/core.c
kernel/sched/deadline.c
kernel/sched/sched.h

index ffb6eb55cd135b26aa8e0564efa843f02fbe86b4..b2e30fbbeef051a33d58581ab6119d2d1191f955 100644 (file)
@@ -1847,6 +1847,8 @@ current_restore_flags(unsigned long orig_flags, unsigned long flags)
 
 extern int cpuset_cpumask_can_shrink(const struct cpumask *cur, const struct cpumask *trial);
 extern int task_can_attach(struct task_struct *p, const struct cpumask *cs_effective_cpus);
+extern int dl_bw_alloc(int cpu, u64 dl_bw);
+extern void dl_bw_free(int cpu, u64 dl_bw);
 #ifdef CONFIG_SMP
 extern void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask);
 extern int set_cpus_allowed_ptr(struct task_struct *p, const struct cpumask *new_mask);
index 8a8dbb2dac0314b3e9c9c2975aed87b8bbcdd6ce..6963fc4ef8976bdb2e1e202d92e359266722ac63 100644 (file)
@@ -9108,7 +9108,7 @@ int task_can_attach(struct task_struct *p,
 
                if (unlikely(cpu >= nr_cpu_ids))
                        return -EINVAL;
-               ret = dl_cpu_busy(cpu, p);
+               ret = dl_bw_alloc(cpu, p->dl.dl_bw);
        }
 
 out:
@@ -9393,7 +9393,7 @@ static void cpuset_cpu_active(void)
 static int cpuset_cpu_inactive(unsigned int cpu)
 {
        if (!cpuhp_tasks_frozen) {
-               int ret = dl_cpu_busy(cpu, NULL);
+               int ret = dl_bw_check_overflow(cpu);
 
                if (ret)
                        return ret;
index 98154a93e05d32c992e63f7acf7da01221c0c7e2..9ce9810861ba595a5d514ce9b1694baebbe582a1 100644 (file)
@@ -3037,26 +3037,38 @@ int dl_cpuset_cpumask_can_shrink(const struct cpumask *cur,
        return ret;
 }
 
-int dl_cpu_busy(int cpu, struct task_struct *p)
+enum dl_bw_request {
+       dl_bw_req_check_overflow = 0,
+       dl_bw_req_alloc,
+       dl_bw_req_free
+};
+
+static int dl_bw_manage(enum dl_bw_request req, int cpu, u64 dl_bw)
 {
-       unsigned long flags, cap;
+       unsigned long flags;
        struct dl_bw *dl_b;
-       bool overflow;
+       bool overflow = 0;
 
        rcu_read_lock_sched();
        dl_b = dl_bw_of(cpu);
        raw_spin_lock_irqsave(&dl_b->lock, flags);
-       cap = dl_bw_capacity(cpu);
-       overflow = __dl_overflow(dl_b, cap, 0, p ? p->dl.dl_bw : 0);
 
-       if (!overflow && p) {
-               /*
-                * We reserve space for this task in the destination
-                * root_domain, as we can't fail after this point.
-                * We will free resources in the source root_domain
-                * later on (see set_cpus_allowed_dl()).
-                */
-               __dl_add(dl_b, p->dl.dl_bw, dl_bw_cpus(cpu));
+       if (req == dl_bw_req_free) {
+               __dl_sub(dl_b, dl_bw, dl_bw_cpus(cpu));
+       } else {
+               unsigned long cap = dl_bw_capacity(cpu);
+
+               overflow = __dl_overflow(dl_b, cap, 0, dl_bw);
+
+               if (req == dl_bw_req_alloc && !overflow) {
+                       /*
+                        * We reserve space in the destination
+                        * root_domain, as we can't fail after this point.
+                        * We will free resources in the source root_domain
+                        * later on (see set_cpus_allowed_dl()).
+                        */
+                       __dl_add(dl_b, dl_bw, dl_bw_cpus(cpu));
+               }
        }
 
        raw_spin_unlock_irqrestore(&dl_b->lock, flags);
@@ -3064,6 +3076,21 @@ int dl_cpu_busy(int cpu, struct task_struct *p)
 
        return overflow ? -EBUSY : 0;
 }
+
+int dl_bw_check_overflow(int cpu)
+{
+       return dl_bw_manage(dl_bw_req_check_overflow, cpu, 0);
+}
+
+int dl_bw_alloc(int cpu, u64 dl_bw)
+{
+       return dl_bw_manage(dl_bw_req_alloc, cpu, dl_bw);
+}
+
+void dl_bw_free(int cpu, u64 dl_bw)
+{
+       dl_bw_manage(dl_bw_req_free, cpu, dl_bw);
+}
 #endif
 
 #ifdef CONFIG_SCHED_DEBUG
index d6d488e8eb554b17e5e8fd973dca10450d765dca..b62d53d7c264f4293091ab599bfe4af1b7f2b00c 100644 (file)
@@ -330,7 +330,7 @@ extern void __getparam_dl(struct task_struct *p, struct sched_attr *attr);
 extern bool __checkparam_dl(const struct sched_attr *attr);
 extern bool dl_param_changed(struct task_struct *p, const struct sched_attr *attr);
 extern int  dl_cpuset_cpumask_can_shrink(const struct cpumask *cur, const struct cpumask *trial);
-extern int  dl_cpu_busy(int cpu, struct task_struct *p);
+extern int  dl_bw_check_overflow(int cpu);
 
 #ifdef CONFIG_CGROUP_SCHED