18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  kernel/sched/cpudl.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Global CPU deadline management
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Author: Juri Lelli <j.lelli@sssup.it>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#include "sched.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_cistatic inline int parent(int i)
128c2ecf20Sopenharmony_ci{
138c2ecf20Sopenharmony_ci	return (i - 1) >> 1;
148c2ecf20Sopenharmony_ci}
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic inline int left_child(int i)
178c2ecf20Sopenharmony_ci{
188c2ecf20Sopenharmony_ci	return (i << 1) + 1;
198c2ecf20Sopenharmony_ci}
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic inline int right_child(int i)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	return (i << 1) + 2;
248c2ecf20Sopenharmony_ci}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic void cpudl_heapify_down(struct cpudl *cp, int idx)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	int l, r, largest;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	int orig_cpu = cp->elements[idx].cpu;
318c2ecf20Sopenharmony_ci	u64 orig_dl = cp->elements[idx].dl;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	if (left_child(idx) >= cp->size)
348c2ecf20Sopenharmony_ci		return;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	/* adapted from lib/prio_heap.c */
378c2ecf20Sopenharmony_ci	while (1) {
388c2ecf20Sopenharmony_ci		u64 largest_dl;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci		l = left_child(idx);
418c2ecf20Sopenharmony_ci		r = right_child(idx);
428c2ecf20Sopenharmony_ci		largest = idx;
438c2ecf20Sopenharmony_ci		largest_dl = orig_dl;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci		if ((l < cp->size) && dl_time_before(orig_dl,
468c2ecf20Sopenharmony_ci						cp->elements[l].dl)) {
478c2ecf20Sopenharmony_ci			largest = l;
488c2ecf20Sopenharmony_ci			largest_dl = cp->elements[l].dl;
498c2ecf20Sopenharmony_ci		}
508c2ecf20Sopenharmony_ci		if ((r < cp->size) && dl_time_before(largest_dl,
518c2ecf20Sopenharmony_ci						cp->elements[r].dl))
528c2ecf20Sopenharmony_ci			largest = r;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci		if (largest == idx)
558c2ecf20Sopenharmony_ci			break;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci		/* pull largest child onto idx */
588c2ecf20Sopenharmony_ci		cp->elements[idx].cpu = cp->elements[largest].cpu;
598c2ecf20Sopenharmony_ci		cp->elements[idx].dl = cp->elements[largest].dl;
608c2ecf20Sopenharmony_ci		cp->elements[cp->elements[idx].cpu].idx = idx;
618c2ecf20Sopenharmony_ci		idx = largest;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci	/* actual push down of saved original values orig_* */
648c2ecf20Sopenharmony_ci	cp->elements[idx].cpu = orig_cpu;
658c2ecf20Sopenharmony_ci	cp->elements[idx].dl = orig_dl;
668c2ecf20Sopenharmony_ci	cp->elements[cp->elements[idx].cpu].idx = idx;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic void cpudl_heapify_up(struct cpudl *cp, int idx)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	int p;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	int orig_cpu = cp->elements[idx].cpu;
748c2ecf20Sopenharmony_ci	u64 orig_dl = cp->elements[idx].dl;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	if (idx == 0)
778c2ecf20Sopenharmony_ci		return;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	do {
808c2ecf20Sopenharmony_ci		p = parent(idx);
818c2ecf20Sopenharmony_ci		if (dl_time_before(orig_dl, cp->elements[p].dl))
828c2ecf20Sopenharmony_ci			break;
838c2ecf20Sopenharmony_ci		/* pull parent onto idx */
848c2ecf20Sopenharmony_ci		cp->elements[idx].cpu = cp->elements[p].cpu;
858c2ecf20Sopenharmony_ci		cp->elements[idx].dl = cp->elements[p].dl;
868c2ecf20Sopenharmony_ci		cp->elements[cp->elements[idx].cpu].idx = idx;
878c2ecf20Sopenharmony_ci		idx = p;
888c2ecf20Sopenharmony_ci	} while (idx != 0);
898c2ecf20Sopenharmony_ci	/* actual push up of saved original values orig_* */
908c2ecf20Sopenharmony_ci	cp->elements[idx].cpu = orig_cpu;
918c2ecf20Sopenharmony_ci	cp->elements[idx].dl = orig_dl;
928c2ecf20Sopenharmony_ci	cp->elements[cp->elements[idx].cpu].idx = idx;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic void cpudl_heapify(struct cpudl *cp, int idx)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	if (idx > 0 && dl_time_before(cp->elements[parent(idx)].dl,
988c2ecf20Sopenharmony_ci				cp->elements[idx].dl))
998c2ecf20Sopenharmony_ci		cpudl_heapify_up(cp, idx);
1008c2ecf20Sopenharmony_ci	else
1018c2ecf20Sopenharmony_ci		cpudl_heapify_down(cp, idx);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic inline int cpudl_maximum(struct cpudl *cp)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	return cp->elements[0].cpu;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/*
1108c2ecf20Sopenharmony_ci * cpudl_find - find the best (later-dl) CPU in the system
1118c2ecf20Sopenharmony_ci * @cp: the cpudl max-heap context
1128c2ecf20Sopenharmony_ci * @p: the task
1138c2ecf20Sopenharmony_ci * @later_mask: a mask to fill in with the selected CPUs (or NULL)
1148c2ecf20Sopenharmony_ci *
1158c2ecf20Sopenharmony_ci * Returns: int - CPUs were found
1168c2ecf20Sopenharmony_ci */
1178c2ecf20Sopenharmony_ciint cpudl_find(struct cpudl *cp, struct task_struct *p,
1188c2ecf20Sopenharmony_ci	       struct cpumask *later_mask)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	const struct sched_dl_entity *dl_se = &p->dl;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	if (later_mask &&
1238c2ecf20Sopenharmony_ci	    cpumask_and(later_mask, cp->free_cpus, p->cpus_ptr)) {
1248c2ecf20Sopenharmony_ci		unsigned long cap, max_cap = 0;
1258c2ecf20Sopenharmony_ci		int cpu, max_cpu = -1;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci		if (!static_branch_unlikely(&sched_asym_cpucapacity))
1288c2ecf20Sopenharmony_ci			return 1;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci		/* Ensure the capacity of the CPUs fits the task. */
1318c2ecf20Sopenharmony_ci		for_each_cpu(cpu, later_mask) {
1328c2ecf20Sopenharmony_ci			if (!dl_task_fits_capacity(p, cpu)) {
1338c2ecf20Sopenharmony_ci				cpumask_clear_cpu(cpu, later_mask);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci				cap = capacity_orig_of(cpu);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci				if (cap > max_cap ||
1388c2ecf20Sopenharmony_ci				    (cpu == task_cpu(p) && cap == max_cap)) {
1398c2ecf20Sopenharmony_ci					max_cap = cap;
1408c2ecf20Sopenharmony_ci					max_cpu = cpu;
1418c2ecf20Sopenharmony_ci				}
1428c2ecf20Sopenharmony_ci			}
1438c2ecf20Sopenharmony_ci		}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci		if (cpumask_empty(later_mask))
1468c2ecf20Sopenharmony_ci			cpumask_set_cpu(max_cpu, later_mask);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci		return 1;
1498c2ecf20Sopenharmony_ci	} else {
1508c2ecf20Sopenharmony_ci		int best_cpu = cpudl_maximum(cp);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci		WARN_ON(best_cpu != -1 && !cpu_present(best_cpu));
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci		if (cpumask_test_cpu(best_cpu, p->cpus_ptr) &&
1558c2ecf20Sopenharmony_ci		    dl_time_before(dl_se->deadline, cp->elements[0].dl)) {
1568c2ecf20Sopenharmony_ci			if (later_mask)
1578c2ecf20Sopenharmony_ci				cpumask_set_cpu(best_cpu, later_mask);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci			return 1;
1608c2ecf20Sopenharmony_ci		}
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci	return 0;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/*
1668c2ecf20Sopenharmony_ci * cpudl_clear - remove a CPU from the cpudl max-heap
1678c2ecf20Sopenharmony_ci * @cp: the cpudl max-heap context
1688c2ecf20Sopenharmony_ci * @cpu: the target CPU
1698c2ecf20Sopenharmony_ci *
1708c2ecf20Sopenharmony_ci * Notes: assumes cpu_rq(cpu)->lock is locked
1718c2ecf20Sopenharmony_ci *
1728c2ecf20Sopenharmony_ci * Returns: (void)
1738c2ecf20Sopenharmony_ci */
1748c2ecf20Sopenharmony_civoid cpudl_clear(struct cpudl *cp, int cpu)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	int old_idx, new_cpu;
1778c2ecf20Sopenharmony_ci	unsigned long flags;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	WARN_ON(!cpu_present(cpu));
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&cp->lock, flags);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	old_idx = cp->elements[cpu].idx;
1848c2ecf20Sopenharmony_ci	if (old_idx == IDX_INVALID) {
1858c2ecf20Sopenharmony_ci		/*
1868c2ecf20Sopenharmony_ci		 * Nothing to remove if old_idx was invalid.
1878c2ecf20Sopenharmony_ci		 * This could happen if a rq_offline_dl is
1888c2ecf20Sopenharmony_ci		 * called for a CPU without -dl tasks running.
1898c2ecf20Sopenharmony_ci		 */
1908c2ecf20Sopenharmony_ci	} else {
1918c2ecf20Sopenharmony_ci		new_cpu = cp->elements[cp->size - 1].cpu;
1928c2ecf20Sopenharmony_ci		cp->elements[old_idx].dl = cp->elements[cp->size - 1].dl;
1938c2ecf20Sopenharmony_ci		cp->elements[old_idx].cpu = new_cpu;
1948c2ecf20Sopenharmony_ci		cp->size--;
1958c2ecf20Sopenharmony_ci		cp->elements[new_cpu].idx = old_idx;
1968c2ecf20Sopenharmony_ci		cp->elements[cpu].idx = IDX_INVALID;
1978c2ecf20Sopenharmony_ci		cpudl_heapify(cp, old_idx);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci		cpumask_set_cpu(cpu, cp->free_cpus);
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&cp->lock, flags);
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci/*
2058c2ecf20Sopenharmony_ci * cpudl_set - update the cpudl max-heap
2068c2ecf20Sopenharmony_ci * @cp: the cpudl max-heap context
2078c2ecf20Sopenharmony_ci * @cpu: the target CPU
2088c2ecf20Sopenharmony_ci * @dl: the new earliest deadline for this CPU
2098c2ecf20Sopenharmony_ci *
2108c2ecf20Sopenharmony_ci * Notes: assumes cpu_rq(cpu)->lock is locked
2118c2ecf20Sopenharmony_ci *
2128c2ecf20Sopenharmony_ci * Returns: (void)
2138c2ecf20Sopenharmony_ci */
2148c2ecf20Sopenharmony_civoid cpudl_set(struct cpudl *cp, int cpu, u64 dl)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	int old_idx;
2178c2ecf20Sopenharmony_ci	unsigned long flags;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	WARN_ON(!cpu_present(cpu));
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&cp->lock, flags);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	old_idx = cp->elements[cpu].idx;
2248c2ecf20Sopenharmony_ci	if (old_idx == IDX_INVALID) {
2258c2ecf20Sopenharmony_ci		int new_idx = cp->size++;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci		cp->elements[new_idx].dl = dl;
2288c2ecf20Sopenharmony_ci		cp->elements[new_idx].cpu = cpu;
2298c2ecf20Sopenharmony_ci		cp->elements[cpu].idx = new_idx;
2308c2ecf20Sopenharmony_ci		cpudl_heapify_up(cp, new_idx);
2318c2ecf20Sopenharmony_ci		cpumask_clear_cpu(cpu, cp->free_cpus);
2328c2ecf20Sopenharmony_ci	} else {
2338c2ecf20Sopenharmony_ci		cp->elements[old_idx].dl = dl;
2348c2ecf20Sopenharmony_ci		cpudl_heapify(cp, old_idx);
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&cp->lock, flags);
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci/*
2418c2ecf20Sopenharmony_ci * cpudl_set_freecpu - Set the cpudl.free_cpus
2428c2ecf20Sopenharmony_ci * @cp: the cpudl max-heap context
2438c2ecf20Sopenharmony_ci * @cpu: rd attached CPU
2448c2ecf20Sopenharmony_ci */
2458c2ecf20Sopenharmony_civoid cpudl_set_freecpu(struct cpudl *cp, int cpu)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	cpumask_set_cpu(cpu, cp->free_cpus);
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci/*
2518c2ecf20Sopenharmony_ci * cpudl_clear_freecpu - Clear the cpudl.free_cpus
2528c2ecf20Sopenharmony_ci * @cp: the cpudl max-heap context
2538c2ecf20Sopenharmony_ci * @cpu: rd attached CPU
2548c2ecf20Sopenharmony_ci */
2558c2ecf20Sopenharmony_civoid cpudl_clear_freecpu(struct cpudl *cp, int cpu)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	cpumask_clear_cpu(cpu, cp->free_cpus);
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci/*
2618c2ecf20Sopenharmony_ci * cpudl_init - initialize the cpudl structure
2628c2ecf20Sopenharmony_ci * @cp: the cpudl max-heap context
2638c2ecf20Sopenharmony_ci */
2648c2ecf20Sopenharmony_ciint cpudl_init(struct cpudl *cp)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	int i;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	raw_spin_lock_init(&cp->lock);
2698c2ecf20Sopenharmony_ci	cp->size = 0;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	cp->elements = kcalloc(nr_cpu_ids,
2728c2ecf20Sopenharmony_ci			       sizeof(struct cpudl_item),
2738c2ecf20Sopenharmony_ci			       GFP_KERNEL);
2748c2ecf20Sopenharmony_ci	if (!cp->elements)
2758c2ecf20Sopenharmony_ci		return -ENOMEM;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (!zalloc_cpumask_var(&cp->free_cpus, GFP_KERNEL)) {
2788c2ecf20Sopenharmony_ci		kfree(cp->elements);
2798c2ecf20Sopenharmony_ci		return -ENOMEM;
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	for_each_possible_cpu(i)
2838c2ecf20Sopenharmony_ci		cp->elements[i].idx = IDX_INVALID;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	return 0;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci/*
2898c2ecf20Sopenharmony_ci * cpudl_cleanup - clean up the cpudl structure
2908c2ecf20Sopenharmony_ci * @cp: the cpudl max-heap context
2918c2ecf20Sopenharmony_ci */
2928c2ecf20Sopenharmony_civoid cpudl_cleanup(struct cpudl *cp)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	free_cpumask_var(cp->free_cpus);
2958c2ecf20Sopenharmony_ci	kfree(cp->elements);
2968c2ecf20Sopenharmony_ci}
297