The current SCHED_DEADLINE (DL) scheduler uses a global EDF scheduling algorithm w/o considering CPU capacity or task utilization. This works well on homogeneous systems where DL tasks are guaranteed to have a bounded tardiness but presents issues on heterogeneous systems. A DL task can migrate to a CPU which does not have enough CPU capacity to correctly serve the task (e.g. a task w/ 70ms runtime and 100ms period on a CPU w/ 512 capacity). Add the DL fitness function dl_task_fits_capacity() for DL admission control on heterogeneous systems. A task fits onto a CPU if: CPU original capacity / 1024 >= task runtime / task deadline Use this function on heterogeneous systems to try to find a CPU which meets this criterion during task wakeup, push and offline migration. On homogeneous systems the original behavior of the DL admission control should be retained.
Showing the changes to kernel (3 of 3 files). Show all
Unified · Split
| @@ -121,7 +121,19 @@ int cpudl_find(struct cpudl *cp, struct task_struct *p, · 120 unmodified lines | |||
| 121 | 121 | ||
| 122 | if (later_mask && | 122 | if (later_mask && |
| 123 | cpumask_and(later_mask, cp->free_cpus, p->cpus_ptr)) { | 123 | cpumask_and(later_mask, cp->free_cpus, p->cpus_ptr)) { |
| 124 | return 1; | 124 | int cpu; |
| 125 | |||
| 126 | if (!static_branch_unlikely(&sched_asym_cpucapacity)) | ||
| 127 | return 1; | ||
| 128 | |||
| 129 | /* Ensure the capacity of the CPUs fits the task. */ | ||
| 130 | for_each_cpu(cpu, later_mask) { | ||
| 131 | if (!dl_task_fits_capacity(p, cpu)) | ||
| 132 | cpumask_clear_cpu(cpu, later_mask); | ||
| 133 | } | ||
| 134 | |||
| 135 | if (!cpumask_empty(later_mask)) | ||
| 136 | return 1; | ||
| 125 | } else { | 137 | } else { |
| 126 | int best_cpu = cpudl_maximum(cp); | 138 | int best_cpu = cpudl_maximum(cp); |
| 127 | 139 | ||
| @@ -1643,6 +1643,7 @@ static int · 1642 unmodified lines | |||
| 1643 | select_task_rq_dl(struct task_struct *p, int cpu, int sd_flag, int flags) | 1643 | select_task_rq_dl(struct task_struct *p, int cpu, int sd_flag, int flags) |
| 1644 | { | 1644 | { |
| 1645 | struct task_struct *curr; | 1645 | struct task_struct *curr; |
| 1646 | bool select_rq; | ||
| 1646 | struct rq *rq; | 1647 | struct rq *rq; |
| 1647 | 1648 | ||
| 1648 | if (sd_flag != SD_BALANCE_WAKE) | 1649 | if (sd_flag != SD_BALANCE_WAKE) |
| @@ -1662,10 +1663,19 @@ select_task_rq_dl(struct task_struct *p, int cpu, int sd_flag, int flags) · 13 unmodified lines | |||
| 1662 | * other hand, if it has a shorter deadline, we | 1663 | * other hand, if it has a shorter deadline, we |
| 1663 | * try to make it stay here, it might be important. | 1664 | * try to make it stay here, it might be important. |
| 1664 | */ | 1665 | */ |
| 1665 | if (unlikely(dl_task(curr)) && | 1666 | select_rq = unlikely(dl_task(curr)) && |
| 1666 | (curr->nr_cpus_allowed < 2 || | 1667 | (curr->nr_cpus_allowed < 2 || |
| 1667 | !dl_entity_preempt(&p->dl, &curr->dl)) && | 1668 | !dl_entity_preempt(&p->dl, &curr->dl)) && |
| 1668 | (p->nr_cpus_allowed > 1)) { | 1669 | p->nr_cpus_allowed > 1; |
| 1670 | |||
| 1671 | /* | ||
| 1672 | * Take the capacity of the CPU into account to | ||
| 1673 | * ensure it fits the requirement of the task. | ||
| 1674 | */ | ||
| 1675 | if (static_branch_unlikely(&sched_asym_cpucapacity)) | ||
| 1676 | select_rq |= !dl_task_fits_capacity(p, cpu); | ||
| 1677 | |||
| 1678 | if (select_rq) { | ||
| 1669 | int target = find_later_rq(p); | 1679 | int target = find_later_rq(p); |
| 1670 | 1680 | ||
| 1671 | if (target != -1 && | 1681 | if (target != -1 && |
| @@ -317,6 +317,21 @@ static inline bool __dl_overflow(struct dl_bw *dl_b, unsigned long cap, · 316 unmodified lines | |||
| 317 | cap_scale(dl_b->bw, cap) < dl_b->total_bw - old_bw + new_bw; | 317 | cap_scale(dl_b->bw, cap) < dl_b->total_bw - old_bw + new_bw; |
| 318 | } | 318 | } |
| 319 | 319 | ||
| 320 | /* | ||
| 321 | * Verify the fitness of task @p to run on @cpu taking into account the | ||
| 322 | * CPU original capacity and the runtime/deadline ratio of the task. | ||
| 323 | * | ||
| 324 | * The function will return true if the CPU original capacity of the | ||
| 325 | * @cpu scaled by SCHED_CAPACITY_SCALE >= runtime/deadline ratio of the | ||
| 326 | * task and false otherwise. | ||
| 327 | */ | ||
| 328 | static inline bool dl_task_fits_capacity(struct task_struct *p, int cpu) | ||
| 329 | { | ||
| 330 | unsigned long cap = arch_scale_cpu_capacity(cpu); | ||
| 331 | |||
| 332 | return cap_scale(p->dl.dl_deadline, cap) >= p->dl.dl_runtime; | ||
| 333 | } | ||
| 334 | |||
| 320 | extern void init_dl_bw(struct dl_bw *dl_b); | 335 | extern void init_dl_bw(struct dl_bw *dl_b); |
| 321 | extern int sched_dl_global_validate(void); | 336 | extern int sched_dl_global_validate(void); |
| 322 | extern void sched_dl_do_global(void); | 337 | extern void sched_dl_do_global(void); |