LCOV - code coverage report
Current view: top level - kernel/sched - cpudeadline.c (source / functions) Hit Total Coverage
Test: landlock.info Lines: 24 132 18.2 %
Date: 2021-04-22 12:43:58 Functions: 4 10 40.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0-only
       2             : /*
       3             :  *  kernel/sched/cpudl.c
       4             :  *
       5             :  *  Global CPU deadline management
       6             :  *
       7             :  *  Author: Juri Lelli <j.lelli@sssup.it>
       8             :  */
       9             : #include "sched.h"
      10             : 
      11           0 : static inline int parent(int i)
      12             : {
      13           0 :         return (i - 1) >> 1;
      14             : }
      15             : 
      16           0 : static inline int left_child(int i)
      17             : {
      18           0 :         return (i << 1) + 1;
      19             : }
      20             : 
      21           0 : static inline int right_child(int i)
      22             : {
      23           0 :         return (i << 1) + 2;
      24             : }
      25             : 
      26           0 : static void cpudl_heapify_down(struct cpudl *cp, int idx)
      27             : {
      28           0 :         int l, r, largest;
      29             : 
      30           0 :         int orig_cpu = cp->elements[idx].cpu;
      31           0 :         u64 orig_dl = cp->elements[idx].dl;
      32             : 
      33           0 :         if (left_child(idx) >= cp->size)
      34             :                 return;
      35             : 
      36             :         /* adapted from lib/prio_heap.c */
      37           0 :         while (1) {
      38           0 :                 u64 largest_dl;
      39             : 
      40           0 :                 l = left_child(idx);
      41           0 :                 r = right_child(idx);
      42           0 :                 largest = idx;
      43           0 :                 largest_dl = orig_dl;
      44             : 
      45           0 :                 if ((l < cp->size) && dl_time_before(orig_dl,
      46           0 :                                                 cp->elements[l].dl)) {
      47           0 :                         largest = l;
      48           0 :                         largest_dl = cp->elements[l].dl;
      49             :                 }
      50           0 :                 if ((r < cp->size) && dl_time_before(largest_dl,
      51           0 :                                                 cp->elements[r].dl))
      52           0 :                         largest = r;
      53             : 
      54           0 :                 if (largest == idx)
      55             :                         break;
      56             : 
      57             :                 /* pull largest child onto idx */
      58           0 :                 cp->elements[idx].cpu = cp->elements[largest].cpu;
      59           0 :                 cp->elements[idx].dl = cp->elements[largest].dl;
      60           0 :                 cp->elements[cp->elements[idx].cpu].idx = idx;
      61           0 :                 idx = largest;
      62             :         }
      63             :         /* actual push down of saved original values orig_* */
      64           0 :         cp->elements[idx].cpu = orig_cpu;
      65           0 :         cp->elements[idx].dl = orig_dl;
      66           0 :         cp->elements[cp->elements[idx].cpu].idx = idx;
      67             : }
      68             : 
      69           0 : static void cpudl_heapify_up(struct cpudl *cp, int idx)
      70             : {
      71           0 :         int p;
      72             : 
      73           0 :         int orig_cpu = cp->elements[idx].cpu;
      74           0 :         u64 orig_dl = cp->elements[idx].dl;
      75             : 
      76           0 :         if (idx == 0)
      77             :                 return;
      78             : 
      79           0 :         do {
      80           0 :                 p = parent(idx);
      81           0 :                 if (dl_time_before(orig_dl, cp->elements[p].dl))
      82             :                         break;
      83             :                 /* pull parent onto idx */
      84           0 :                 cp->elements[idx].cpu = cp->elements[p].cpu;
      85           0 :                 cp->elements[idx].dl = cp->elements[p].dl;
      86           0 :                 cp->elements[cp->elements[idx].cpu].idx = idx;
      87           0 :                 idx = p;
      88           0 :         } while (idx != 0);
      89             :         /* actual push up of saved original values orig_* */
      90           0 :         cp->elements[idx].cpu = orig_cpu;
      91           0 :         cp->elements[idx].dl = orig_dl;
      92           0 :         cp->elements[cp->elements[idx].cpu].idx = idx;
      93             : }
      94             : 
      95           0 : static void cpudl_heapify(struct cpudl *cp, int idx)
      96             : {
      97           0 :         if (idx > 0 && dl_time_before(cp->elements[parent(idx)].dl,
      98           0 :                                 cp->elements[idx].dl))
      99           0 :                 cpudl_heapify_up(cp, idx);
     100             :         else
     101           0 :                 cpudl_heapify_down(cp, idx);
     102           0 : }
     103             : 
     104           0 : static inline int cpudl_maximum(struct cpudl *cp)
     105             : {
     106           0 :         return cp->elements[0].cpu;
     107             : }
     108             : 
     109             : /*
     110             :  * cpudl_find - find the best (later-dl) CPU in the system
     111             :  * @cp: the cpudl max-heap context
     112             :  * @p: the task
     113             :  * @later_mask: a mask to fill in with the selected CPUs (or NULL)
     114             :  *
     115             :  * Returns: int - CPUs were found
     116             :  */
     117           0 : int cpudl_find(struct cpudl *cp, struct task_struct *p,
     118             :                struct cpumask *later_mask)
     119             : {
     120           0 :         const struct sched_dl_entity *dl_se = &p->dl;
     121             : 
     122           0 :         if (later_mask &&
     123           0 :             cpumask_and(later_mask, cp->free_cpus, &p->cpus_mask)) {
     124           0 :                 unsigned long cap, max_cap = 0;
     125           0 :                 int cpu, max_cpu = -1;
     126             : 
     127           0 :                 if (!static_branch_unlikely(&sched_asym_cpucapacity))
     128             :                         return 1;
     129             : 
     130             :                 /* Ensure the capacity of the CPUs fits the task. */
     131           0 :                 for_each_cpu(cpu, later_mask) {
     132           0 :                         if (!dl_task_fits_capacity(p, cpu)) {
     133           0 :                                 cpumask_clear_cpu(cpu, later_mask);
     134             : 
     135           0 :                                 cap = capacity_orig_of(cpu);
     136             : 
     137           0 :                                 if (cap > max_cap ||
     138           0 :                                     (cpu == task_cpu(p) && cap == max_cap)) {
     139             :                                         max_cap = cap;
     140             :                                         max_cpu = cpu;
     141             :                                 }
     142             :                         }
     143             :                 }
     144             : 
     145           0 :                 if (cpumask_empty(later_mask))
     146           0 :                         cpumask_set_cpu(max_cpu, later_mask);
     147             : 
     148           0 :                 return 1;
     149             :         } else {
     150           0 :                 int best_cpu = cpudl_maximum(cp);
     151             : 
     152           0 :                 WARN_ON(best_cpu != -1 && !cpu_present(best_cpu));
     153             : 
     154           0 :                 if (cpumask_test_cpu(best_cpu, &p->cpus_mask) &&
     155           0 :                     dl_time_before(dl_se->deadline, cp->elements[0].dl)) {
     156           0 :                         if (later_mask)
     157           0 :                                 cpumask_set_cpu(best_cpu, later_mask);
     158             : 
     159           0 :                         return 1;
     160             :                 }
     161             :         }
     162             :         return 0;
     163             : }
     164             : 
     165             : /*
     166             :  * cpudl_clear - remove a CPU from the cpudl max-heap
     167             :  * @cp: the cpudl max-heap context
     168             :  * @cpu: the target CPU
     169             :  *
     170             :  * Notes: assumes cpu_rq(cpu)->lock is locked
     171             :  *
     172             :  * Returns: (void)
     173             :  */
     174           4 : void cpudl_clear(struct cpudl *cp, int cpu)
     175             : {
     176           4 :         int old_idx, new_cpu;
     177           4 :         unsigned long flags;
     178             : 
     179           4 :         WARN_ON(!cpu_present(cpu));
     180             : 
     181           4 :         raw_spin_lock_irqsave(&cp->lock, flags);
     182             : 
     183           4 :         old_idx = cp->elements[cpu].idx;
     184           4 :         if (old_idx == IDX_INVALID) {
     185             :                 /*
     186             :                  * Nothing to remove if old_idx was invalid.
     187             :                  * This could happen if a rq_offline_dl is
     188             :                  * called for a CPU without -dl tasks running.
     189             :                  */
     190             :         } else {
     191           0 :                 new_cpu = cp->elements[cp->size - 1].cpu;
     192           0 :                 cp->elements[old_idx].dl = cp->elements[cp->size - 1].dl;
     193           0 :                 cp->elements[old_idx].cpu = new_cpu;
     194           0 :                 cp->size--;
     195           0 :                 cp->elements[new_cpu].idx = old_idx;
     196           0 :                 cp->elements[cpu].idx = IDX_INVALID;
     197           0 :                 cpudl_heapify(cp, old_idx);
     198             : 
     199           0 :                 cpumask_set_cpu(cpu, cp->free_cpus);
     200             :         }
     201           4 :         raw_spin_unlock_irqrestore(&cp->lock, flags);
     202           4 : }
     203             : 
     204             : /*
     205             :  * cpudl_set - update the cpudl max-heap
     206             :  * @cp: the cpudl max-heap context
     207             :  * @cpu: the target CPU
     208             :  * @dl: the new earliest deadline for this CPU
     209             :  *
     210             :  * Notes: assumes cpu_rq(cpu)->lock is locked
     211             :  *
     212             :  * Returns: (void)
     213             :  */
     214           0 : void cpudl_set(struct cpudl *cp, int cpu, u64 dl)
     215             : {
     216           0 :         int old_idx;
     217           0 :         unsigned long flags;
     218             : 
     219           0 :         WARN_ON(!cpu_present(cpu));
     220             : 
     221           0 :         raw_spin_lock_irqsave(&cp->lock, flags);
     222             : 
     223           0 :         old_idx = cp->elements[cpu].idx;
     224           0 :         if (old_idx == IDX_INVALID) {
     225           0 :                 int new_idx = cp->size++;
     226             : 
     227           0 :                 cp->elements[new_idx].dl = dl;
     228           0 :                 cp->elements[new_idx].cpu = cpu;
     229           0 :                 cp->elements[cpu].idx = new_idx;
     230           0 :                 cpudl_heapify_up(cp, new_idx);
     231           0 :                 cpumask_clear_cpu(cpu, cp->free_cpus);
     232             :         } else {
     233           0 :                 cp->elements[old_idx].dl = dl;
     234           0 :                 cpudl_heapify(cp, old_idx);
     235             :         }
     236             : 
     237           0 :         raw_spin_unlock_irqrestore(&cp->lock, flags);
     238           0 : }
     239             : 
     240             : /*
     241             :  * cpudl_set_freecpu - Set the cpudl.free_cpus
     242             :  * @cp: the cpudl max-heap context
     243             :  * @cpu: rd attached CPU
     244             :  */
     245           8 : void cpudl_set_freecpu(struct cpudl *cp, int cpu)
     246             : {
     247           8 :         cpumask_set_cpu(cpu, cp->free_cpus);
     248           8 : }
     249             : 
     250             : /*
     251             :  * cpudl_clear_freecpu - Clear the cpudl.free_cpus
     252             :  * @cp: the cpudl max-heap context
     253             :  * @cpu: rd attached CPU
     254             :  */
     255           4 : void cpudl_clear_freecpu(struct cpudl *cp, int cpu)
     256             : {
     257           4 :         cpumask_clear_cpu(cpu, cp->free_cpus);
     258           4 : }
     259             : 
     260             : /*
     261             :  * cpudl_init - initialize the cpudl structure
     262             :  * @cp: the cpudl max-heap context
     263             :  */
     264           2 : int cpudl_init(struct cpudl *cp)
     265             : {
     266           2 :         int i;
     267             : 
     268           2 :         raw_spin_lock_init(&cp->lock);
     269           2 :         cp->size = 0;
     270             : 
     271           2 :         cp->elements = kcalloc(nr_cpu_ids,
     272             :                                sizeof(struct cpudl_item),
     273             :                                GFP_KERNEL);
     274           2 :         if (!cp->elements)
     275             :                 return -ENOMEM;
     276             : 
     277           2 :         if (!zalloc_cpumask_var(&cp->free_cpus, GFP_KERNEL)) {
     278             :                 kfree(cp->elements);
     279             :                 return -ENOMEM;
     280             :         }
     281             : 
     282          10 :         for_each_possible_cpu(i)
     283           8 :                 cp->elements[i].idx = IDX_INVALID;
     284             : 
     285             :         return 0;
     286             : }
     287             : 
     288             : /*
     289             :  * cpudl_cleanup - clean up the cpudl structure
     290             :  * @cp: the cpudl max-heap context
     291             :  */
     292           0 : void cpudl_cleanup(struct cpudl *cp)
     293             : {
     294           0 :         free_cpumask_var(cp->free_cpus);
     295           0 :         kfree(cp->elements);
     296           0 : }

Generated by: LCOV version 1.14