| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Completely Fair Scheduling (CFS) Class (SCHED_NORMAL/SCHED_BATCH) |
| * |
| * Copyright (C) 2007 Red Hat, Inc., Ingo Molnar <mingo@redhat.com> |
| * |
| * Interactivity improvements by Mike Galbraith |
| * (C) 2007 Mike Galbraith <efault@gmx.de> |
| * |
| * Various enhancements by Dmitry Adamushko. |
| * (C) 2007 Dmitry Adamushko <dmitry.adamushko@gmail.com> |
| * |
| * Group scheduling enhancements by Srivatsa Vaddagiri |
| * Copyright IBM Corporation, 2007 |
| * Author: Srivatsa Vaddagiri <vatsa@linux.vnet.ibm.com> |
| * |
| * Scaled math optimizations by Thomas Gleixner |
| * Copyright (C) 2007, Thomas Gleixner <tglx@linutronix.de> |
| * |
| * Adaptive scheduling granularity, math enhancements by Peter Zijlstra |
| * Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra |
| */ |
| #include <linux/energy_model.h> |
| #include <linux/mmap_lock.h> |
| #include <linux/hugetlb_inline.h> |
| #include <linux/jiffies.h> |
| #include <linux/mm_api.h> |
| #include <linux/highmem.h> |
| #include <linux/spinlock_api.h> |
| #include <linux/cpumask_api.h> |
| #include <linux/lockdep_api.h> |
| #include <linux/softirq.h> |
| #include <linux/refcount_api.h> |
| #include <linux/topology.h> |
| #include <linux/sched/clock.h> |
| #include <linux/sched/cond_resched.h> |
| #include <linux/sched/cputime.h> |
| #include <linux/sched/isolation.h> |
| #include <linux/sched/nohz.h> |
| |
| #include <linux/cpuidle.h> |
| #include <linux/interrupt.h> |
| #include <linux/memory-tiers.h> |
| #include <linux/mempolicy.h> |
| #include <linux/mutex_api.h> |
| #include <linux/profile.h> |
| #include <linux/psi.h> |
| #include <linux/ratelimit.h> |
| #include <linux/task_work.h> |
| #include <linux/rbtree_augmented.h> |
| |
| #include <asm/switch_to.h> |
| |
| #include "sched.h" |
| #include "stats.h" |
| #include "autogroup.h" |
| |
| /* |
| * The initial- and re-scaling of tunables is configurable |
| * |
| * Options are: |
| * |
| * SCHED_TUNABLESCALING_NONE - unscaled, always *1 |
| * SCHED_TUNABLESCALING_LOG - scaled logarithmically, *1+ilog(ncpus) |
| * SCHED_TUNABLESCALING_LINEAR - scaled linear, *ncpus |
| * |
| * (default SCHED_TUNABLESCALING_LOG = *(1+ilog(ncpus)) |
| */ |
| unsigned int sysctl_sched_tunable_scaling = SCHED_TUNABLESCALING_LOG; |
| |
| /* |
| * Minimal preemption granularity for CPU-bound tasks: |
| * |
| * (default: 0.75 msec * (1 + ilog(ncpus)), units: nanoseconds) |
| */ |
| unsigned int sysctl_sched_base_slice = 750000ULL; |
| static unsigned int normalized_sysctl_sched_base_slice = 750000ULL; |
| |
| const_debug unsigned int sysctl_sched_migration_cost = 500000UL; |
| |
| static int __init setup_sched_thermal_decay_shift(char *str) |
| { |
| pr_warn("Ignoring the deprecated sched_thermal_decay_shift= option\n"); |
| return 1; |
| } |
| __setup("sched_thermal_decay_shift=", setup_sched_thermal_decay_shift); |
| |
| #ifdef CONFIG_SMP |
| /* |
| * For asym packing, by default the lower numbered CPU has higher priority. |
| */ |
| int __weak arch_asym_cpu_priority(int cpu) |
| { |
| return -cpu; |
| } |
| |
| /* |
| * The margin used when comparing utilization with CPU capacity. |
| * |
| * (default: ~20%) |
| */ |
| #define fits_capacity(cap, max) ((cap) * 1280 < (max) * 1024) |
| |
| /* |
| * The margin used when comparing CPU capacities. |
| * is 'cap1' noticeably greater than 'cap2' |
| * |
| * (default: ~5%) |
| */ |
| #define capacity_greater(cap1, cap2) ((cap1) * 1024 > (cap2) * 1078) |
| #endif |
| |
| #ifdef CONFIG_CFS_BANDWIDTH |
| /* |
| * Amount of runtime to allocate from global (tg) to local (per-cfs_rq) pool |
| * each time a cfs_rq requests quota. |
| * |
| * Note: in the case that the slice exceeds the runtime remaining (either due |
| * to consumption or the quota being specified to be smaller than the slice) |
| * we will always only issue the remaining available time. |
| * |
| * (default: 5 msec, units: microseconds) |
| */ |
| static unsigned int sysctl_sched_cfs_bandwidth_slice = 5000UL; |
| #endif |
| |
| #ifdef CONFIG_NUMA_BALANCING |
| /* Restrict the NUMA promotion throughput (MB/s) for each target node. */ |
| static unsigned int sysctl_numa_balancing_promote_rate_limit = 65536; |
| #endif |
| |
| #ifdef CONFIG_SYSCTL |
| static struct ctl_table sched_fair_sysctls[] = { |
| #ifdef CONFIG_CFS_BANDWIDTH |
| { |
| .procname = "sched_cfs_bandwidth_slice_us", |
| .data = &sysctl_sched_cfs_bandwidth_slice, |
| .maxlen = sizeof(unsigned int), |
| .mode = 0644, |
| .proc_handler = proc_dointvec_minmax, |
| .extra1 = SYSCTL_ONE, |
| }, |
| #endif |
| #ifdef CONFIG_NUMA_BALANCING |
| { |
| .procname = "numa_balancing_promote_rate_limit_MBps", |
| .data = &sysctl_numa_balancing_promote_rate_limit, |
| .maxlen = sizeof(unsigned int), |
| .mode = 0644, |
| .proc_handler = proc_dointvec_minmax, |
| .extra1 = SYSCTL_ZERO, |
| }, |
| #endif /* CONFIG_NUMA_BALANCING */ |
| }; |
| |
| static int __init sched_fair_sysctl_init(void) |
| { |
| register_sysctl_init("kernel", sched_fair_sysctls); |
| return 0; |
| } |
| late_initcall(sched_fair_sysctl_init); |
| #endif |
| |
| static inline void update_load_add(struct load_weight *lw, unsigned long inc) |
| { |
| lw->weight += inc; |
| lw->inv_weight = 0; |
| } |
| |
| static inline void update_load_sub(struct load_weight *lw, unsigned long dec) |
| { |
| lw->weight -= dec; |
| lw->inv_weight = 0; |
| } |
| |
| static inline void update_load_set(struct load_weight *lw, unsigned long w) |
| { |
| lw->weight = w; |
| lw->inv_weight = 0; |
| } |
| |
| /* |
| * Increase the granularity value when there are more CPUs, |
| * because with more CPUs the 'effective latency' as visible |
| * to users decreases. But the relationship is not linear, |
| * so pick a second-best guess by going with the log2 of the |
| * number of CPUs. |
| * |
| * This idea comes from the SD scheduler of Con Kolivas: |
| */ |
| static unsigned int get_update_sysctl_factor(void) |
| { |
| unsigned int cpus = min_t(unsigned int, num_online_cpus(), 8); |
| unsigned int factor; |
| |
| switch (sysctl_sched_tunable_scaling) { |
| case SCHED_TUNABLESCALING_NONE: |
| factor = 1; |
| break; |
| case SCHED_TUNABLESCALING_LINEAR: |
| factor = cpus; |
| break; |
| case SCHED_TUNABLESCALING_LOG: |
| default: |
| factor = 1 + ilog2(cpus); |
| break; |
| } |
| |
| return factor; |
| } |
| |
| static void update_sysctl(void) |
| { |
| unsigned int factor = get_update_sysctl_factor(); |
| |
| #define SET_SYSCTL(name) \ |
| (sysctl_##name = (factor) * normalized_sysctl_##name) |
| SET_SYSCTL(sched_base_slice); |
| #undef SET_SYSCTL |
| } |
| |
| void __init sched_init_granularity(void) |
| { |
| update_sysctl(); |
| } |
| |
| #define WMULT_CONST (~0U) |
| #define WMULT_SHIFT 32 |
| |
| static void __update_inv_weight(struct load_weight *lw) |
| { |
| unsigned long w; |
| |
| if (likely(lw->inv_weight)) |
| return; |
| |
| w = scale_load_down(lw->weight); |
| |
| if (BITS_PER_LONG > 32 && unlikely(w >= WMULT_CONST)) |
| lw->inv_weight = 1; |
| else if (unlikely(!w)) |
| lw->inv_weight = WMULT_CONST; |
| else |
| lw->inv_weight = WMULT_CONST / w; |
| } |
| |
| /* |
| * delta_exec * weight / lw.weight |
| * OR |
| * (delta_exec * (weight * lw->inv_weight)) >> WMULT_SHIFT |
| * |
| * Either weight := NICE_0_LOAD and lw \e sched_prio_to_wmult[], in which case |
| * we're guaranteed shift stays positive because inv_weight is guaranteed to |
| * fit 32 bits, and NICE_0_LOAD gives another 10 bits; therefore shift >= 22. |
| * |
| * Or, weight =< lw.weight (because lw.weight is the runqueue weight), thus |
| * weight/lw.weight <= 1, and therefore our shift will also be positive. |
| */ |
| static u64 __calc_delta(u64 delta_exec, unsigned long weight, struct load_weight *lw) |
| { |
| u64 fact = scale_load_down(weight); |
| u32 fact_hi = (u32)(fact >> 32); |
| int shift = WMULT_SHIFT; |
| int fs; |
| |
| __update_inv_weight(lw); |
| |
| if (unlikely(fact_hi)) { |
| fs = fls(fact_hi); |
| shift -= fs; |
| fact >>= fs; |
| } |
| |
| fact = mul_u32_u32(fact, lw->inv_weight); |
| |
| fact_hi = (u32)(fact >> 32); |
| if (fact_hi) { |
| fs = fls(fact_hi); |
| shift -= fs; |
| fact >>= fs; |
| } |
| |
| return mul_u64_u32_shr(delta_exec, fact, shift); |
| } |
| |
| /* |
| * delta /= w |
| */ |
| static inline u64 calc_delta_fair(u64 delta, struct sched_entity *se) |
| { |
| if (unlikely(se->load.weight != NICE_0_LOAD)) |
| delta = __calc_delta(delta, NICE_0_LOAD, &se->load); |
| |
| return delta; |
| } |
| |
| const struct sched_class fair_sched_class; |
| |
| /************************************************************** |
| * CFS operations on generic schedulable entities: |
| */ |
| |
| #ifdef CONFIG_FAIR_GROUP_SCHED |
| |
| /* Walk up scheduling entities hierarchy */ |
| #define for_each_sched_entity(se) \ |
| for (; se; se = se->parent) |
| |
| static inline bool list_add_leaf_cfs_rq(struct cfs_rq *cfs_rq) |
| { |
| struct rq *rq = rq_of(cfs_rq); |
| int cpu = cpu_of(rq); |
| |
| if (cfs_rq->on_list) |
| return rq->tmp_alone_branch == &rq->leaf_cfs_rq_list; |
| |
| cfs_rq->on_list = 1; |
| |
| /* |
| * Ensure we either appear before our parent (if already |
| * enqueued) or force our parent to appear after us when it is |
| * enqueued. The fact that we always enqueue bottom-up |
| * reduces this to two cases and a special case for the root |
| * cfs_rq. Furthermore, it also means that we will always reset |
| * tmp_alone_branch either when the branch is connected |
| * to a tree or when we reach the top of the tree |
| */ |
| if (cfs_rq->tg->parent && |
| cfs_rq->tg->parent->cfs_rq[cpu]->on_list) { |
| /* |
| * If parent is already on the list, we add the child |
| * just before. Thanks to circular linked property of |
| * the list, this means to put the child at the tail |
| * of the list that starts by parent. |
| */ |
| list_add_tail_rcu(&cfs_rq->leaf_cfs_rq_list, |
| &(cfs_rq->tg->parent->cfs_rq[cpu]->leaf_cfs_rq_list)); |
| /* |
| * The branch is now connected to its tree so we can |
| * reset tmp_alone_branch to the beginning of the |
| * list. |
| */ |
| rq->tmp_alone_branch = &rq->leaf_cfs_rq_list; |
| return true; |
| } |
| |
| if (!cfs_rq->tg->parent) { |
| /* |
| * cfs rq without parent should be put |
| * at the tail of the list. |
| */ |
| list_add_tail_rcu(&cfs_rq->leaf_cfs_rq_list, |
| &rq->leaf_cfs_rq_list); |
| /* |
| * We have reach the top of a tree so we can reset |
| * tmp_alone_branch to the beginning of the list. |
| */ |
| rq->tmp_alone_branch = &rq->leaf_cfs_rq_list; |
| return true; |
| } |
| |
| /* |
| * The parent has not already been added so we want to |
| * make sure that it will be put after us. |
| * tmp_alone_branch points to the begin of the branch |
| * where we will add parent. |
| */ |
| list_add_rcu(&cfs_rq->leaf_cfs_rq_list, rq->tmp_alone_branch); |
| /* |
| * update tmp_alone_branch to points to the new begin |
| * of the branch |
| */ |
| rq->tmp_alone_branch = &cfs_rq->leaf_cfs_rq_list; |
| return false; |
| } |
| |
| static inline void list_del_leaf_cfs_rq(struct cfs_rq *cfs_rq) |
| { |
| if (cfs_rq->on_list) { |
| struct rq *rq = rq_of(cfs_rq); |
| |
| /* |
| * With cfs_rq being unthrottled/throttled during an enqueue, |
| * it can happen the tmp_alone_branch points to the leaf that |
| * we finally want to delete. In this case, tmp_alone_branch moves |
| * to the prev element but it will point to rq->leaf_cfs_rq_list |
| * at the end of the enqueue. |
| */ |
| if (rq->tmp_alone_branch == &cfs_rq->leaf_cfs_rq_list) |
| rq->tmp_alone_branch = cfs_rq->leaf_cfs_rq_list.prev; |
| |
| list_del_rcu(&cfs_rq->leaf_cfs_rq_list); |
| cfs_rq->on_list = 0; |
| } |
| } |
| |
| static inline void assert_list_leaf_cfs_rq(struct rq *rq) |
| { |
| SCHED_WARN_ON(rq->tmp_alone_branch != &rq->leaf_cfs_rq_list); |
| } |
| |
| /* Iterate through all leaf cfs_rq's on a runqueue */ |
| #define for_each_leaf_cfs_rq_safe(rq, cfs_rq, pos) \ |
| list_for_each_entry_safe(cfs_rq, pos, &rq->leaf_cfs_rq_list, \ |
| leaf_cfs_rq_list) |
| |
| /* Do the two (enqueued) entities belong to the same group ? */ |
| static inline struct cfs_rq * |
| is_same_group(struct sched_entity *se, struct sched_entity *pse) |
| { |
| if (se->cfs_rq == pse->cfs_rq) |
| return se->cfs_rq; |
| |
| return NULL; |
| } |
| |
| static inline struct sched_entity *parent_entity(const struct sched_entity *se) |
| { |
| return se->parent; |
| } |
| |
| static void |
| find_matching_se(struct sched_entity **se, struct sched_entity **pse) |
| { |
| int se_depth, pse_depth; |
| |
| /* |
| * preemption test can be made between sibling entities who are in the |
| * same cfs_rq i.e who have a common parent. Walk up the hierarchy of |
| * both tasks until we find their ancestors who are siblings of common |
| * parent. |
| */ |
| |
| /* First walk up until both entities are at same depth */ |
| se_depth = (*se)->depth; |
| pse_depth = (*pse)->depth; |
| |
| while (se_depth > pse_depth) { |
| se_depth--; |
| *se = parent_entity(*se); |
| } |
| |
| while (pse_depth > se_depth) { |
| pse_depth--; |
| *pse = parent_entity(*pse); |
| } |
| |
| while (!is_same_group(*se, *pse)) { |
| *se = parent_entity(*se); |
| *pse = parent_entity(*pse); |
| } |
| } |
| |
| static int tg_is_idle(struct task_group *tg) |
| { |
| return tg->idle > 0; |
| } |
| |
| static int cfs_rq_is_idle(struct cfs_rq *cfs_rq) |
| { |
| return cfs_rq->idle > 0; |
| } |
| |
| static int se_is_idle(struct sched_entity *se) |
| { |
| if (entity_is_task(se)) |
| return task_has_idle_policy(task_of(se)); |
| return cfs_rq_is_idle(group_cfs_rq(se)); |
| } |
| |
| #else /* !CONFIG_FAIR_GROUP_SCHED */ |
| |
| #define for_each_sched_entity(se) \ |
| for (; se; se = NULL) |
| |
| static inline bool list_add_leaf_cfs_rq(struct cfs_rq *cfs_rq) |
| { |
| return true; |
| } |
| |
| static inline void list_del_leaf_cfs_rq(struct cfs_rq *cfs_rq) |
| { |
| } |
| |
| static inline void assert_list_leaf_cfs_rq(struct rq *rq) |
| { |
| } |
| |
| #define for_each_leaf_cfs_rq_safe(rq, cfs_rq, pos) \ |
| for (cfs_rq = &rq->cfs, pos = NULL; cfs_rq; cfs_rq = pos) |
| |
| static inline struct sched_entity *parent_entity(struct sched_entity *se) |
| { |
| return NULL; |
| } |
| |
| static inline void |
| find_matching_se(struct sched_entity **se, struct sched_entity **pse) |
| { |
| } |
| |
| static inline int tg_is_idle(struct task_group *tg) |
| { |
| return 0; |
| } |
| |
| static int cfs_rq_is_idle(struct cfs_rq *cfs_rq) |
| { |
| return 0; |
| } |
| |
| static int se_is_idle(struct sched_entity *se) |
| { |
| return 0; |
| } |
| |
| #endif /* CONFIG_FAIR_GROUP_SCHED */ |
| |
| static __always_inline |
| void account_cfs_rq_runtime(struct cfs_rq *cfs_rq, u64 delta_exec); |
| |
| /************************************************************** |
| * Scheduling class tree data structure manipulation methods: |
| */ |
| |
| static inline u64 max_vruntime(u64 max_vruntime, u64 vruntime) |
| { |
| s64 delta = (s64)(vruntime - max_vruntime); |
| if (delta > 0) |
| max_vruntime = vruntime; |
| |
| return max_vruntime; |
| } |
| |
| static inline u64 min_vruntime(u64 min_vruntime, u64 vruntime) |
| { |
| s64 delta = (s64)(vruntime - min_vruntime); |
| if (delta < 0) |
| min_vruntime = vruntime; |
| |
| return min_vruntime; |
| } |
| |
| static inline bool entity_before(const struct sched_entity *a, |
| const struct sched_entity *b) |
| { |
| /* |
| * Tiebreak on vruntime seems unnecessary since it can |
| * hardly happen. |
| */ |
| return (s64)(a->deadline - b->deadline) < 0; |
| } |
| |
| static inline s64 entity_key(struct cfs_rq *cfs_rq, struct sched_entity *se) |
| { |
| return (s64)(se->vruntime - cfs_rq->min_vruntime); |
| } |
| |
| #define __node_2_se(node) \ |
| rb_entry((node), struct sched_entity, run_node) |
| |
| /* |
| * Compute virtual time from the per-task service numbers: |
| * |
| * Fair schedulers conserve lag: |
| * |
| * \Sum lag_i = 0 |
| * |
| * Where lag_i is given by: |
| * |
| * lag_i = S - s_i = w_i * (V - v_i) |
| * |
| * Where S is the ideal service time and V is it's virtual time counterpart. |
| * Therefore: |
| * |
| * \Sum lag_i = 0 |
| * \Sum w_i * (V - v_i) = 0 |
| * \Sum w_i * V - w_i * v_i = 0 |
| * |
| * From which we can solve an expression for V in v_i (which we have in |
| * se->vruntime): |
| * |
| * \Sum v_i * w_i \Sum v_i * w_i |
| * V = -------------- = -------------- |
| * \Sum w_i W |
| * |
| * Specifically, this is the weighted average of all entity virtual runtimes. |
| * |
| * [[ NOTE: this is only equal to the ideal scheduler under the condition |
| * that join/leave operations happen at lag_i = 0, otherwise the |
| * virtual time has non-contiguous motion equivalent to: |
| * |
| * V +-= lag_i / W |
| * |
| * Also see the comment in place_entity() that deals with this. ]] |
| * |
| * However, since v_i is u64, and the multiplication could easily overflow |
| * transform it into a relative form that uses smaller quantities: |
| * |
| * Substitute: v_i == (v_i - v0) + v0 |
| * |
| * \Sum ((v_i - v0) + v0) * w_i \Sum (v_i - v0) * w_i |
| * V = ---------------------------- = --------------------- + v0 |
| * W W |
| * |
| * Which we track using: |
| * |
| * v0 := cfs_rq->min_vruntime |
| * \Sum (v_i - v0) * w_i := cfs_rq->avg_vruntime |
| * \Sum w_i := cfs_rq->avg_load |
| * |
| * Since min_vruntime is a monotonic increasing variable that closely tracks |
| * the per-task service, these deltas: (v_i - v), will be in the order of the |
| * maximal (virtual) lag induced in the system due to quantisation. |
| * |
| * Also, we use scale_load_down() to reduce the size. |
| * |
| * As measured, the max (key * weight) value was ~44 bits for a kernel build. |
| */ |
| static void |
| avg_vruntime_add(struct cfs_rq *cfs_rq, struct sched_entity *se) |
| { |
| unsigned long weight = scale_load_down(se->load.weight); |
| s64 key = entity_key(cfs_rq, se); |
| |
| cfs_rq->avg_vruntime += key * weight; |
| cfs_rq->avg_load += weight; |
| } |
| |
| static void |
| avg_vruntime_sub(struct cfs_rq *cfs_rq, struct sched_entity *se) |
| { |
| unsigned long weight = scale_load_down(se->load.weight); |
| s64 key = entity_key(cfs_rq, se); |
| |
| cfs_rq->avg_vruntime -= key * weight; |
| cfs_rq->avg_load -= weight; |
| } |
| |
| static inline |
| void avg_vruntime_update(struct cfs_rq *cfs_rq, s64 delta) |
| { |
| /* |
| * v' = v + d ==> avg_vruntime' = avg_runtime - d*avg_load |
| */ |
| cfs_rq->avg_vruntime -= cfs_rq->avg_load * delta; |
| } |
| |
| /* |
| * Specifically: avg_runtime() + 0 must result in entity_eligible() := true |
| * For this to be so, the result of this function must have a left bias. |
| */ |
| u64 avg_vruntime(struct cfs_rq *cfs_rq) |
| { |
| struct sched_entity *curr = cfs_rq->curr; |
| s64 avg = cfs_rq->avg_vruntime; |
| long load = cfs_rq->avg_load; |
| |
| if (curr && curr->on_rq) { |
| unsigned long weight = scale_load_down(curr->load.weight); |
| |
| avg += entity_key(cfs_rq, curr) * weight; |
| load += weight; |
| } |
| |
| if (load) { |
| /* sign flips effective floor / ceiling */ |
| if (avg < 0) |
| avg -= (load - 1); |
| avg = div_s64(avg, load); |
| } |
| |
| return cfs_rq->min_vruntime + avg; |
| } |
| |
| /* |
| * lag_i = S - s_i = w_i * (V - v_i) |
| * |
| * However, since V is approximated by the weighted average of all entities it |
| * is possible -- by addition/removal/reweight to the tree -- to move V around |
| * and end up with a larger lag than we started with. |
| * |
| * Limit this to either double the slice length with a minimum of TICK_NSEC |
| * since that is the timing granularity. |
| * |
| * EEVDF gives the following limit for a steady state system: |
| * |
| * -r_max < lag < max(r_max, q) |
| * |
| * XXX could add max_slice to the augmented data to track this. |
| */ |
| static s64 entity_lag(u64 avruntime, struct sched_entity *se) |
| { |
| s64 vlag, limit; |
| |
| vlag = avruntime - se->vruntime; |
| limit = calc_delta_fair(max_t(u64, 2*se->slice, TICK_NSEC), se); |
| |
| return clamp(vlag, -limit, limit); |
| } |
| |
| static void update_entity_lag(struct cfs_rq *cfs_rq, struct sched_entity *se) |
| { |
| SCHED_WARN_ON(!se->on_rq); |
| |
| se->vlag = entity_lag(avg_vruntime(cfs_rq), se); |
| } |
| |
| /* |
| * Entity is eligible once it received less service than it ought to have, |
| * eg. lag >= 0. |
| * |
| * lag_i = S - s_i = w_i*(V - v_i) |
| * |
| * lag_i >= 0 -> V >= v_i |
| * |
| * \Sum (v_i - v)*w_i |
| * V = ------------------ + v |
| * \Sum w_i |
| * |
| * lag_i >= 0 -> \Sum (v_i - v)*w_i >= (v_i - v)*(\Sum w_i) |
| * |
| * Note: using 'avg_vruntime() > se->vruntime' is inaccurate due |
| * to the loss in precision caused by the division. |
| */ |
| static int vruntime_eligible(struct cfs_rq *cfs_rq, u64 vruntime) |
| { |
| struct sched_entity *curr = cfs_rq->curr; |
| s64 avg = cfs_rq->avg_vruntime; |
| long load = cfs_rq->avg_load; |
| |
| if (curr && curr->on_rq) { |
| unsigned long weight = scale_load_down(curr->load.weight); |
| |
| avg += entity_key(cfs_rq, curr) * weight; |
| load += weight; |
| } |
| |
| return avg >= (s64)(vruntime - cfs_rq->min_vruntime) * load; |
| } |
| |
| int entity_eligible(struct cfs_rq *cfs_rq, struct sched_entity *se) |
| { |
| return vruntime_eligible(cfs_rq, se->vruntime); |
| } |
| |
| static u64 __update_min_vruntime(struct cfs_rq *cfs_rq, u64 vruntime) |
| { |
| u64 min_vruntime = cfs_rq->min_vruntime; |
| /* |
| * open coded max_vruntime() to allow updating avg_vruntime |
| */ |
| s64 delta = (s64)(vruntime - min_vruntime); |
| if (delta > 0) { |
| avg_vruntime_update(cfs_rq, delta); |
| min_vruntime = vruntime; |
| } |
| return min_vruntime; |
| } |
| |
| static void update_min_vruntime(struct cfs_rq *cfs_rq) |
| { |
| struct sched_entity *se = __pick_root_entity(cfs_rq); |
| struct sched_entity *curr = cfs_rq->curr; |
| u64 vruntime = cfs_rq->min_vruntime; |
| |
| if (curr) { |
| if (curr->on_rq) |
| vruntime = curr->vruntime; |
| else |
| curr = NULL; |
| } |
| |
| if (se) { |
| if (!curr) |
| vruntime = se->min_vruntime; |
| else |
| vruntime = min_vruntime(vruntime, se->min_vruntime); |
| } |
| |
| /* ensure we never gain time by being placed backwards. */ |
| u64_u32_store(cfs_rq->min_vruntime, |
| __update_min_vruntime(cfs_rq, vruntime)); |
| } |
| |
| static inline bool __entity_less(struct rb_node *a, const struct rb_node *b) |
| { |
| return entity_before(__node_2_se(a), __node_2_se(b)); |
| } |
| |
| #define vruntime_gt(field, lse, rse) ({ (s64)((lse)->field - (rse)->field) > 0; }) |
| |
| static inline void __min_vruntime_update(struct sched_entity *se, struct rb_node *node) |
| { |
| if (node) { |
| struct sched_entity *rse = __node_2_se(node); |
| if (vruntime_gt(min_vruntime, se, rse)) |
| se->min_vruntime = rse->min_vruntime; |
| } |
| } |
| |
| /* |
| * se->min_vruntime = min(se->vruntime, {left,right}->min_vruntime) |
| */ |
| static inline bool min_vruntime_update(struct sched_entity *se, bool exit) |
| { |
| u64 old_min_vruntime = se->min_vruntime; |
| struct rb_node *node = &se->run_node; |
| |
| se->min_vruntime = se->vruntime; |
| __min_vruntime_update(se, node->rb_right); |
| __min_vruntime_update(se, node->rb_left); |
| |
| return se->min_vruntime == old_min_vruntime; |
| } |
| |
| RB_DECLARE_CALLBACKS(static, min_vruntime_cb, struct sched_entity, |
| run_node, min_vruntime, min_vruntime_update); |
| |
| /* |
| * Enqueue an entity into the rb-tree: |
| */ |
| static void __enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) |
| { |
| avg_vruntime_add(cfs_rq, se); |
| se->min_vruntime = se->vruntime; |
| rb_add_augmented_cached(&se->run_node, &cfs_rq->tasks_timeline, |
| __entity_less, &min_vruntime_cb); |
| } |
| |
| static void __dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) |
| { |
| rb_erase_augmented_cached(&se->run_node, &cfs_rq->tasks_timeline, |
| &min_vruntime_cb); |
| avg_vruntime_sub(cfs_rq, se); |
| } |
| |
| struct sched_entity *__pick_root_entity(struct cfs_rq *cfs_rq) |
| { |
| struct rb_node *root = cfs_rq->tasks_timeline.rb_root.rb_node; |
| |
| if (!root) |
| return NULL; |
| |
| return __node_2_se(root); |
| } |
| |
| struct sched_entity *__pick_first_entity(struct cfs_rq *cfs_rq) |
| { |
| struct rb_node *left = rb_first_cached(&cfs_rq->tasks_timeline); |
| |
| if (!left) |
| return NULL; |
| |
| return __node_2_se(left); |
| } |
| |
| /* |
| * Earliest Eligible Virtual Deadline First |
| * |
| * In order to provide latency guarantees for different request sizes |
| * EEVDF selects the best runnable task from two criteria: |
| * |
| * 1) the task must be eligible (must be owed service) |
| * |
| * 2) from those tasks that meet 1), we select the one |
| * with the earliest virtual deadline. |
| * |
| * We can do this in O(log n) time due to an augmented RB-tree. The |
| * tree keeps the entries sorted on deadline, but also functions as a |
| * heap based on the vruntime by keeping: |
| * |
| * se->min_vruntime = min(se->vruntime, se->{left,right}->min_vruntime) |
| * |
| * Which allows tree pruning through eligibility. |
| */ |
| static struct sched_entity *pick_eevdf(struct cfs_rq *cfs_rq) |
| { |
| struct rb_node *node = cfs_rq->tasks_timeline.rb_root.rb_node; |
| struct sched_entity *se = __pick_first_entity(cfs_rq); |
| struct sched_entity *curr = cfs_rq->curr; |
| struct sched_entity *best = NULL; |
| |
| /* |
| * We can safely skip eligibility check if there is only one entity |
| * in this cfs_rq, saving some cycles. |
| */ |
| if (cfs_rq->nr_running == 1) |
| return curr && curr->on_rq ? curr : se; |
| |
| if (curr && (!curr->on_rq || !entity_eligible(cfs_rq, curr))) |
| curr = NULL; |
| |
| /* |
| * Once selected, run a task until it either becomes non-eligible or |
| * until it gets a new slice. See the HACK in set_next_entity(). |
| */ |
| if (sched_feat(RUN_TO_PARITY) && curr && curr->vlag == curr->deadline) |
| return curr; |
| |
| /* Pick the leftmost entity if it's eligible */ |
| if (se && entity_eligible(cfs_rq, se)) { |
| best = se; |
| goto found; |
| } |
| |
| /* Heap search for the EEVD entity */ |
| while (node) { |
| struct rb_node *left = node->rb_left; |
| |
| /* |
| * Eligible entities in left subtree are always better |
| * choices, since they have earlier deadlines. |
| */ |
| if (left && vruntime_eligible(cfs_rq, |
| __node_2_se(left)->min_vruntime)) { |
| node = left; |
| continue; |
| } |
| |
| se = __node_2_se(node); |
| |
| /* |
| * The left subtree either is empty or has no eligible |
| * entity, so check the current node since it is the one |
| * with earliest deadline that might be eligible. |
| */ |
| if (entity_eligible(cfs_rq, se)) { |
| best = se; |
| break; |
| } |
| |
| node = node->rb_right; |
| } |
| found: |
| if (!best || (curr && entity_before(curr, best))) |
| best = curr; |
| |
| return best; |
| } |
| |
| #ifdef CONFIG_SCHED_DEBUG |
| struct sched_entity *__pick_last_entity(struct cfs_rq *cfs_rq) |
| { |
| struct rb_node *last = rb_last(&cfs_rq->tasks_timeline.rb_root); |
| |
| if (!last) |
| return NULL; |
| |
| return __node_2_se(last); |
| } |
| |
| /************************************************************** |
| * Scheduling class statistics methods: |
| */ |
| #ifdef CONFIG_SMP |
| int sched_update_scaling(void) |
| { |
| unsigned int factor = get_update_sysctl_factor(); |
| |
| #define WRT_SYSCTL(name) \ |
| (normalized_sysctl_##name = sysctl_##name / (factor)) |
| WRT_SYSCTL(sched_base_slice); |
| #undef WRT_SYSCTL |
| |
| return 0; |
| } |
| #endif |
| #endif |
| |
| static void clear_buddies(struct cfs_rq *cfs_rq, struct sched_entity *se); |
| |
| /* |
| * XXX: strictly: vd_i += N*r_i/w_i such that: vd_i > ve_i |
| * this is probably good enough. |
| */ |
| static void update_deadline(struct cfs_rq *cfs_rq, struct sched_entity *se) |
| { |
| if ((s64)(se->vruntime - se->deadline) < 0) |
| return; |
| |
| /* |
| * For EEVDF the virtual time slope is determined by w_i (iow. |
| * nice) while the request time r_i is determined by |
| * sysctl_sched_base_slice. |
| */ |
| se->slice = sysctl_sched_base_slice; |
| |
| /* |
| * EEVDF: vd_i = ve_i + r_i / w_i |
| */ |
| se->deadline = se->vruntime + calc_delta_fair(se->slice, se); |
| |
| /* |
| * The task has consumed its request, reschedule. |
| */ |
| if (cfs_rq->nr_running > 1) { |
| resched_curr(rq_of(cfs_rq)); |
| clear_buddies(cfs_rq, se); |
| } |
| } |
| |
| #include "pelt.h" |
| #ifdef CONFIG_SMP |
| |
| static int select_idle_sibling(struct task_struct *p, int prev_cpu, int cpu); |
| static unsigned long task_h_load(struct task_struct *p); |
| static unsigned long capacity_of(int cpu); |
| |
| /* Give new sched_entity start runnable values to heavy its load in infant time */ |
| void init_entity_runnable_average(struct sched_entity *se) |
| { |
| struct sched_avg *sa = &se->avg; |
| |
| memset(sa, 0, sizeof(*sa)); |
| |
| /* |
| * Tasks are initialized with full load to be seen as heavy tasks until |
| * they get a chance to stabilize to their real load level. |
| * Group entities are initialized with zero load to reflect the fact that |
| * nothing has been attached to the task group yet. |
| */ |
| if (entity_is_task(se)) |
| sa->load_avg = scale_load_down(se->load.weight); |
| |
| /* when this task is enqueued, it will contribute to its cfs_rq's load_avg */ |
| } |
| |
| /* |
| * With new tasks being created, their initial util_avgs are extrapolated |
| * based on the cfs_rq's current util_avg: |
| * |
| * util_avg = cfs_rq->avg.util_avg / (cfs_rq->avg.load_avg + 1) |
| * * se_weight(se) |
| * |
| * However, in many cases, the above util_avg does not give a desired |
| * value. Moreover, the sum of the util_avgs may be divergent, such |
| * as when the series is a harmonic series. |
| * |
| * To solve this problem, we also cap the util_avg of successive tasks to |
| * only 1/2 of the left utilization budget: |
| * |
| * util_avg_cap = (cpu_scale - cfs_rq->avg.util_avg) / 2^n |
| * |
| * where n denotes the nth task and cpu_scale the CPU capacity. |
| * |
| * For example, for a CPU with 1024 of capacity, a simplest series from |
| * the beginning would be like: |
| * |
| * task util_avg: 512, 256, 128, 64, 32, 16, 8, ... |
| * cfs_rq util_avg: 512, 768, 896, 960, 992, 1008, 1016, ... |
| * |
| * Finally, that extrapolated util_avg is clamped to the cap (util_avg_cap) |
| * if util_avg > util_avg_cap. |
| */ |
| void post_init_entity_util_avg(struct task_struct *p) |
| { |
| struct sched_entity *se = &p->se; |
| struct cfs_rq *cfs_rq = cfs_rq_of(se); |
| struct sched_avg *sa = &se->avg; |
| long cpu_scale = arch_scale_cpu_capacity(cpu_of(rq_of(cfs_rq))); |
| long cap = (long)(cpu_scale - cfs_rq->avg.util_avg) / 2; |
| |
| if (p->sched_class != &fair_sched_class) { |
| /* |
| * For !fair tasks do: |
| * |
| update_cfs_rq_load_avg(now, cfs_rq); |
| attach_entity_load_avg(cfs_rq, se); |
| switched_from_fair(rq, p); |
| * |
| * such that the next switched_to_fair() has the |
| * expected state. |
| */ |
| se->avg.last_update_time = cfs_rq_clock_pelt(cfs_rq); |
| return; |
| } |
| |
| if (cap > 0) { |
| if (cfs_rq->avg.util_avg != 0) { |
| sa->util_avg = cfs_rq->avg.util_avg * se_weight(se); |
| sa->util_avg /= (cfs_rq->avg.load_avg + 1); |
| |
| if (sa->util_avg > cap) |
| sa->util_avg = cap; |
| } else { |
| sa->util_avg = cap; |
| } |
| } |
| |
| sa->runnable_avg = sa->util_avg; |
| } |
| |
| #else /* !CONFIG_SMP */ |
| void init_entity_runnable_average(struct sched_entity *se) |
| { |
| } |
| void post_init_entity_util_avg(struct task_struct *p) |
| { |
| } |
| static void update_tg_load_avg(struct cfs_rq *cfs_rq) |
| { |
| } |
| #endif /* CONFIG_SMP */ |
| |
| static s64 update_curr_se(struct rq *rq, struct sched_entity *curr) |
| { |
| u64 now = rq_clock_task(rq); |
| s64 delta_exec; |
| |
| delta_exec = now - curr->exec_start; |
| if (unlikely(delta_exec <= 0)) |
| return delta_exec; |
| |
| curr->exec_start = now; |
| curr->sum_exec_runtime += delta_exec; |
| |
| if (schedstat_enabled()) { |
| struct sched_statistics *stats; |
| |
| stats = __schedstats_from_se(curr); |
| __schedstat_set(stats->exec_max, |
| max(delta_exec, stats->exec_max)); |
| } |
| |
| return delta_exec; |
| } |
| |
| static inline void update_curr_task(struct task_struct *p, s64 delta_exec) |
| { |
| trace_sched_stat_runtime(p, delta_exec); |
| account_group_exec_runtime(p, delta_exec); |
| cgroup_account_cputime(p, delta_exec); |
| if (p->dl_server) |
| dl_server_update(p->dl_server, delta_exec); |
| } |
| |
| /* |
| * Used by other classes to account runtime. |
| */ |
| s64 update_curr_common(struct rq *rq) |
| { |
| struct task_struct *curr = rq->curr; |
| s64 delta_exec; |
| |
| delta_exec = update_curr_se(rq, &curr->se); |
| if (likely(delta_exec > 0)) |
| update_curr_task(curr, delta_exec); |
| |
| return delta_exec; |
| } |
| |
| /* |
| * Update the current task's runtime statistics. |
| */ |
| static void update_curr(struct cfs_rq *cfs_rq) |
| { |
| struct sched_entity *curr = cfs_rq->curr; |
| s64 delta_exec; |
| |
| if (unlikely(!curr)) |
| return; |
| |
| delta_exec = update_curr_se(rq_of(cfs_rq), curr); |
| if (unlikely(delta_exec <= 0)) |
| return; |
| |
| curr->vruntime += calc_delta_fair(delta_exec, curr); |
| update_deadline(cfs_rq, curr); |
| update_min_vruntime(cfs_rq); |
| |
| if (entity_is_task(curr)) |
| update_curr_task(task_of(curr), delta_exec); |
| |
| account_cfs_rq_runtime(cfs_rq, delta_exec); |
| } |
| |
| static void update_curr_fair(struct rq *rq) |
| { |
| update_curr(cfs_rq_of(&rq->curr->se)); |
| } |
| |
| static inline void |
| update_stats_wait_start_fair(struct cfs_rq *cfs_rq, struct sched_entity *se) |
| { |
| struct sched_statistics *stats; |
| struct task_struct *p = NULL; |
| |
| if (!schedstat_enabled()) |
| return; |
| |
| stats = __schedstats_from_se(se); |
| |
| if (entity_is_task(se)) |
| p = task_of(se); |
| |
| __update_stats_wait_start(rq_of(cfs_rq), p, stats); |
| } |
| |
| static inline void |
| update_stats_wait_end_fair(struct cfs_rq *cfs_rq, struct sched_entity *se) |
| { |
| struct sched_statistics *stats; |
| struct task_struct *p = NULL; |
| |
| if (!schedstat_enabled()) |
| return; |
| |
| stats = __schedstats_from_se(se); |
| |
| /* |
| * When the sched_schedstat changes from 0 to 1, some sched se |
| * maybe already in the runqueue, the se->statistics.wait_start |
| * will be 0.So it will let the delta wrong. We need to avoid this |
| * scenario. |
| */ |
| if (unlikely(!schedstat_val(stats->wait_start))) |
| return; |
| |
| if (entity_is_task(se)) |
| p = task_of(se); |
| |
| __update_stats_wait_end(rq_of(cfs_rq), p, stats); |
| } |
| |
| static inline void |
| update_stats_enqueue_sleeper_fair(struct cfs_rq *cfs_rq, struct sched_entity *se) |
| { |
| struct sched_statistics *stats; |
| struct task_struct *tsk = NULL; |
| |
| if (!schedstat_enabled()) |
| return; |
| |
| stats = __schedstats_from_se(se); |
| |
| if (entity_is_task(se)) |
| tsk = task_of(se); |
| |
| __update_stats_enqueue_sleeper(rq_of(cfs_rq), tsk, stats); |
| } |
| |
| /* |
| * Task is being enqueued - update stats: |
| */ |
| static inline void |
| update_stats_enqueue_fair(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) |
| { |
| if (!schedstat_enabled()) |
| return; |
| |
| /* |
| * Are we enqueueing a waiting task? (for current tasks |
| * a dequeue/enqueue event is a NOP) |
| */ |
| if (se != cfs_rq->curr) |
| update_stats_wait_start_fair(cfs_rq, se); |
| |
| if (flags & ENQUEUE_WAKEUP) |
| update_stats_enqueue_sleeper_fair(cfs_rq, se); |
| } |
| |
| static inline void |
| update_stats_dequeue_fair(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) |
| { |
| |
| if (!schedstat_enabled()) |
| return; |
| |
| /* |
| * Mark the end of the wait period if dequeueing a |
| * waiting task: |
| */ |
| if (se != cfs_rq->curr) |
| update_stats_wait_end_fair(cfs_rq, se); |
| |
| if ((flags & DEQUEUE_SLEEP) && entity_is_task(se)) { |
| struct task_struct *tsk = task_of(se); |
| unsigned int state; |
| |
| /* XXX racy against TTWU */ |
| state = READ_ONCE(tsk->__state); |
| if (state & TASK_INTERRUPTIBLE) |
| __schedstat_set(tsk->stats.sleep_start, |
| rq_clock(rq_of(cfs_rq))); |
| if (state & TASK_UNINTERRUPTIBLE) |
| __schedstat_set(tsk->stats.block_start, |
| rq_clock(rq_of(cfs_rq))); |
| } |
| } |
| |
| /* |
| * We are picking a new current task - update its stats: |
| */ |
| static inline void |
| update_stats_curr_start(struct cfs_rq *cfs_rq, struct sched_entity *se) |
| { |
| /* |
| * We are starting a new run period: |
| */ |
| se->exec_start = rq_clock_task(rq_of(cfs_rq)); |
| } |
| |
| /************************************************** |
| * Scheduling class queueing methods: |
| */ |
| |
| static inline bool is_core_idle(int cpu) |
| { |
| #ifdef CONFIG_SCHED_SMT |
| int sibling; |
| |
| for_each_cpu(sibling, cpu_smt_mask(cpu)) { |
| if (cpu == sibling) |
| continue; |
| |
| if (!idle_cpu(sibling)) |
| return false; |
| } |
| #endif |
| |
| return true; |
| } |
| |
| #ifdef CONFIG_NUMA |
| #define NUMA_IMBALANCE_MIN 2 |
| |
| static inline long |
| adjust_numa_imbalance(int imbalance, int dst_running, int imb_numa_nr) |
| { |
| /* |
| * Allow a NUMA imbalance if busy CPUs is less than the maximum |
| * threshold. Above this threshold, individual tasks may be contending |
| * for both memory bandwidth and any shared HT resources. This is an |
| * approximation as the number of running tasks may not be related to |
| * the number of busy CPUs due to sched_setaffinity. |
| */ |
| if (dst_running > imb_numa_nr) |
| return imbalance; |
| |
| /* |
| * Allow a small imbalance based on a simple pair of communicating |
| * tasks that remain local when the destination is lightly loaded. |
| */ |
| if (imbalance <= NUMA_IMBALANCE_MIN) |
| return 0; |
| |
| return imbalance; |
| } |
| #endif /* CONFIG_NUMA */ |
| |
| #ifdef CONFIG_NUMA_BALANCING |
| /* |
| * Approximate time to scan a full NUMA task in ms. The task scan period is |
| * calculated based on the tasks virtual memory size and |
| * numa_balancing_scan_size. |
| */ |
| unsigned int sysctl_numa_balancing_scan_period_min = 1000; |
| unsigned int sysctl_numa_balancing_scan_period_max = 60000; |
| |
| /* Portion of address space to scan in MB */ |
| unsigned int sysctl_numa_balancing_scan_size = 256; |
| |
| /* Scan @scan_size MB every @scan_period after an initial @scan_delay in ms */ |
| unsigned int sysctl_numa_balancing_scan_delay = 1000; |
| |
| /* The page with hint page fault latency < threshold in ms is considered hot */ |
| unsigned int sysctl_numa_balancing_hot_threshold = MSEC_PER_SEC; |
| |
| struct numa_group { |
| refcount_t refcount; |
| |
| spinlock_t lock; /* nr_tasks, tasks */ |
| int nr_tasks; |
| pid_t gid; |
| int active_nodes; |
| |
| struct rcu_head rcu; |
| unsigned long total_faults; |
| unsigned long max_faults_cpu; |
| /* |
| * faults[] array is split into two regions: faults_mem and faults_cpu. |
| * |
| * Faults_cpu is used to decide whether memory should move |
| * towards the CPU. As a consequence, these stats are weighted |
| * more by CPU use than by memory faults. |
| */ |
| unsigned long faults[]; |
| }; |
| |
| /* |
| * For functions that can be called in multiple contexts that permit reading |
| * ->numa_group (see struct task_struct for locking rules). |
| */ |
| static struct numa_group *deref_task_numa_group(struct task_struct *p) |
| { |
| return rcu_dereference_check(p->numa_group, p == current || |
| (lockdep_is_held(__rq_lockp(task_rq(p))) && !READ_ONCE(p->on_cpu))); |
| } |
| |
| static struct numa_group *deref_curr_numa_group(struct task_struct *p) |
| { |
| return rcu_dereference_protected(p->numa_group, p == current); |
| } |
| |
| static inline unsigned long group_faults_priv(struct numa_group *ng); |
| static inline unsigned long group_faults_shared(struct numa_group *ng); |
| |
| static unsigned int task_nr_scan_windows(struct task_struct *p) |
| { |
| unsigned long rss = 0; |
| unsigned long nr_scan_pages; |
| |
| /* |
| * Calculations based on RSS as non-present and empty pages are skipped |
| * by the PTE scanner and NUMA hinting faults should be trapped based |
| * on resident pages |
| */ |
| nr_scan_pages = sysctl_numa_balancing_scan_size << (20 - PAGE_SHIFT); |
| rss = get_mm_rss(p->mm); |
| if (!rss) |
| rss = nr_scan_pages; |
| |
| rss = round_up(rss, nr_scan_pages); |
| return rss / nr_scan_pages; |
| } |
| |
| /* For sanity's sake, never scan more PTEs than MAX_SCAN_WINDOW MB/sec. */ |
| #define MAX_SCAN_WINDOW 2560 |
| |
| static unsigned int task_scan_min(struct task_struct *p) |
| { |
| unsigned int scan_size = READ_ONCE(sysctl_numa_balancing_scan_size); |
| unsigned int scan, floor; |
| unsigned int windows = 1; |
| |
| if (scan_size < MAX_SCAN_WINDOW) |
| windows = MAX_SCAN_WINDOW / scan_size; |
| floor = 1000 / windows; |
| |
| scan = sysctl_numa_balancing_scan_period_min / task_nr_scan_windows(p); |
| return max_t(unsigned int, floor, scan); |
| } |
| |
| static unsigned int task_scan_start(struct task_struct *p) |
| { |
| unsigned long smin = task_scan_min(p); |
| unsigned long period = smin; |
| struct numa_group *ng; |
| |
| /* Scale the maximum scan period with the amount of shared memory. */ |
| rcu_read_lock(); |
| ng = rcu_dereference(p->numa_group); |
| if (ng) { |
| unsigned long shared = group_faults_shared(ng); |
| unsigned long private = group_faults_priv(ng); |
| |
| period *= refcount_read(&ng->refcount); |
| period *= shared + 1; |
| period /= private + shared + 1; |
| } |
| rcu_read_unlock(); |
| |
| return max(smin, period); |
| } |
| |
| static unsigned int task_scan_max(struct task_struct *p) |
| { |
| unsigned long smin = task_scan_min(p); |
| unsigned long smax; |
| struct numa_group *ng; |
| |
| /* Watch for min being lower than max due to floor calculations */ |
| smax = sysctl_numa_balancing_scan_period_max / task_nr_scan_windows(p); |
| |
| /* Scale the maximum scan period with the amount of shared memory. */ |
| ng = deref_curr_numa_group(p); |
| if (ng) { |
| unsigned long shared = group_faults_shared(ng); |
| unsigned long private = group_faults_priv(ng); |
| unsigned long period = smax; |
| |
| period *= refcount_read(&ng->refcount); |
| period *= shared + 1; |
| period /= private + shared + 1; |
| |
| smax = max(smax, period); |
| } |
| |
| return max(smin, smax); |
| } |
| |
| static void account_numa_enqueue(struct rq *rq, struct task_struct *p) |
| { |
| rq->nr_numa_running += (p->numa_preferred_nid != NUMA_NO_NODE); |
| rq->nr_preferred_running += (p->numa_preferred_nid == task_node(p)); |
| } |
| |
| static void account_numa_dequeue(struct rq *rq, struct task_struct *p) |
| { |
| rq->nr_numa_running -= (p->numa_preferred_nid != NUMA_NO_NODE); |
| rq->nr_preferred_running -= (p->numa_preferred_nid == task_node(p)); |
| } |
| |
| /* Shared or private faults. */ |
| #define NR_NUMA_HINT_FAULT_TYPES 2 |
| |
| /* Memory and CPU locality */ |
| #define NR_NUMA_HINT_FAULT_STATS (NR_NUMA_HINT_FAULT_TYPES * 2) |
| |
| /* Averaged statistics, and temporary buffers. */ |
| #define NR_NUMA_HINT_FAULT_BUCKETS (NR_NUMA_HINT_FAULT_STATS * 2) |
| |
| pid_t task_numa_group_id(struct task_struct *p) |
| { |
| struct numa_group *ng; |
| pid_t gid = 0; |
| |
| rcu_read_lock(); |
| ng = rcu_dereference(p->numa_group); |
| if (ng) |
| gid = ng->gid; |
| rcu_read_unlock(); |
| |
| return gid; |
| } |
| |
| /* |
| * The averaged statistics, shared & private, memory & CPU, |
| * occupy the first half of the array. The second half of the |
| * array is for current counters, which are averaged into the |
| * first set by task_numa_placement. |
| */ |
| static inline int task_faults_idx(enum numa_faults_stats s, int nid, int priv) |
| { |
| return NR_NUMA_HINT_FAULT_TYPES * (s * nr_node_ids + nid) + priv; |
| } |
| |
| static inline unsigned long task_faults(struct task_struct *p, int nid) |
| { |
| if (!p->numa_faults) |
| return 0; |
| |
| return p->numa_faults[task_faults_idx(NUMA_MEM, nid, 0)] + |
| p->numa_faults[task_faults_idx(NUMA_MEM, nid, 1)]; |
| } |
| |
| static inline unsigned long group_faults(struct task_struct *p, int nid) |
| { |
| struct numa_group *ng = deref_task_numa_group(p); |
| |
| if (!ng) |
| return 0; |
| |
| return ng->faults[task_faults_idx(NUMA_MEM, nid, 0)] + |
| ng->faults[task_faults_idx(NUMA_MEM, nid, 1)]; |
| } |
| |
| static inline unsigned long group_faults_cpu(struct numa_group *group, int nid) |
| { |
| return group->faults[task_faults_idx(NUMA_CPU, nid, 0)] + |
| group->faults[task_faults_idx(NUMA_CPU, nid, 1)]; |
| } |
| |
| static inline unsigned long group_faults_priv(struct numa_group *ng) |
| { |
| unsigned long faults = 0; |
| int node; |
| |
| for_each_online_node(node) { |
| faults += ng->faults[task_faults_idx(NUMA_MEM, node, 1)]; |
| } |
| |
| return faults; |
| } |
| |
| static inline unsigned long group_faults_shared(struct numa_group *ng) |
| { |
| unsigned long faults = 0; |
| int node; |
| |
| for_each_online_node(node) { |
| faults += ng->faults[task_faults_idx(NUMA_MEM, node, 0)]; |
| } |
| |
| return faults; |
| } |
| |
| /* |
| * A node triggering more than 1/3 as many NUMA faults as the maximum is |
| * considered part of a numa group's pseudo-interleaving set. Migrations |
| * between these nodes are slowed down, to allow things to settle down. |
| */ |
| #define ACTIVE_NODE_FRACTION 3 |
| |
| static bool numa_is_active_node(int nid, struct numa_group *ng) |
| { |
| return group_faults_cpu(ng, nid) * ACTIVE_NODE_FRACTION > ng->max_faults_cpu; |
| } |
| |
| /* Handle placement on systems where not all nodes are directly connected. */ |
| static unsigned long score_nearby_nodes(struct task_struct *p, int nid, |
| int lim_dist, bool task) |
| { |
| unsigned long score = 0; |
| int node, max_dist; |
| |
| /* |
| * All nodes are directly connected, and the same distance |
| * from each other. No need for fancy placement algorithms. |
| */ |
| if (sched_numa_topology_type == NUMA_DIRECT) |
| return 0; |
| |
| /* sched_max_numa_distance may be changed in parallel. */ |
| max_dist = READ_ONCE(sched_max_numa_distance); |
| /* |
| * This code is called for each node, introducing N^2 complexity, |
| * which should be OK given the number of nodes rarely exceeds 8. |
| */ |
| for_each_online_node(node) { |
| unsigned long faults; |
| int dist = node_distance(nid, node); |
| |
| /* |
| * The furthest away nodes in the system are not interesting |
| * for placement; nid was already counted. |
| */ |
| if (dist >= max_dist || node == nid) |
| continue; |
| |
| /* |
| * On systems with a backplane NUMA topology, compare groups |
| * of nodes, and move tasks towards the group with the most |
| * memory accesses. When comparing two nodes at distance |
| * "hoplimit", only nodes closer by than "hoplimit" are part |
| * of each group. Skip other nodes. |
| */ |
| if (sched_numa_topology_type == NUMA_BACKPLANE && dist >= lim_dist) |
| continue; |
| |
| /* Add up the faults from nearby nodes. */ |
| if (task) |
| faults = task_faults(p, node); |
| else |
| faults = group_faults(p, node); |
| |
| /* |
| * On systems with a glueless mesh NUMA topology, there are |
| * no fixed "groups of nodes". Instead, nodes that are not |
| * directly connected bounce traffic through intermediate |
| * nodes; a numa_group can occupy any set of nodes. |
| * The further away a node is, the less the faults count. |
| * This seems to result in good task placement. |
| */ |
| if (sched_numa_topology_type == NUMA_GLUELESS_MESH) { |
| faults *= (max_dist - dist); |
| faults /= (max_dist - LOCAL_DISTANCE); |
| } |
| |
| score += faults; |
| } |
| |
| return score; |
| } |
| |
| /* |
| * These return the fraction of accesses done by a particular task, or |
| * task group, on a particular numa node. The group weight is given a |
| * larger multiplier, in order to group tasks together that are almost |
| * evenly spread out between numa nodes. |
| */ |
| static inline unsigned long task_weight(struct task_struct *p, int nid, |
| int dist) |
| { |
| unsigned long faults, total_faults; |
| |
| if (!p->numa_faults) |
| return 0; |
| |
| total_faults = p->total_numa_faults; |
| |
| if (!total_faults) |
| return 0; |
| |
| faults = task_faults(p, nid); |
| faults += score_nearby_nodes(p, nid, dist, true); |
| |
| return 1000 * faults / total_faults; |
| } |
| |
| static inline unsigned long group_weight(struct task_struct *p, int nid, |
| int dist) |
| { |
| struct numa_group *ng = deref_task_numa_group(p); |
| unsigned long faults, total_faults; |
| |
| if (!ng) |
| return 0; |
| |
| total_faults = ng->total_faults; |
| |
| if (!total_faults) |
| return 0; |
| |
| faults = group_faults(p, nid); |
| faults += score_nearby_nodes(p, nid, dist, false); |
| |
| return 1000 * faults / total_faults; |
| } |
| |
| /* |
| * If memory tiering mode is enabled, cpupid of slow memory page is |
| * used to record scan time instead of CPU and PID. When tiering mode |
| * is disabled at run time, the scan time (in cpupid) will be |
| * interpreted as CPU and PID. So CPU needs to be checked to avoid to |
| * access out of array bound. |
| */ |
| static inline bool cpupid_valid(int cpupid) |
| { |
| return cpupid_to_cpu(cpupid) < nr_cpu_ids; |
| } |
| |
| /* |
| * For memory tiering mode, if there are enough free pages (more than |
| * enough watermark defined here) in fast memory node, to take full |
| * advantage of fast memory capacity, all recently accessed slow |
| * memory pages will be migrated to fast memory node without |
| * considering hot threshold. |
| */ |
| static bool pgdat_free_space_enough(struct pglist_data *pgdat) |
| { |
| int z; |
| unsigned long enough_wmark; |
| |
| enough_wmark = max(1UL * 1024 * 1024 * 1024 >> PAGE_SHIFT, |
| pgdat->node_present_pages >> 4); |
| for (z = pgdat->nr_zones - 1; z >= 0; z--) { |
| struct zone *zone = pgdat->node_zones + z; |
| |
| if (!populated_zone(zone)) |
| continue; |
| |
| if (zone_watermark_ok(zone, 0, |
| wmark_pages(zone, WMARK_PROMO) + enough_wmark, |
| ZONE_MOVABLE, 0)) |
| return true; |
| } |
| return false; |
| } |
| |
| /* |
| * For memory tiering mode, when page tables are scanned, the scan |
| * time will be recorded in struct page in addition to make page |
| * PROT_NONE for slow memory page. So when the page is accessed, in |
| * hint page fault handler, the hint page fault latency is calculated |
| * via, |
| * |
| * hint page fault latency = hint page fault time - scan time |
| * |
| * The smaller the hint page fault latency, the higher the possibility |
| * for the page to be hot. |
| */ |
| static int numa_hint_fault_latency(struct folio *folio) |
| { |
| int last_time, time; |
| |
| time = jiffies_to_msecs(jiffies); |
| last_time = folio_xchg_access_time(folio, time); |
| |
| return (time - last_time) & PAGE_ACCESS_TIME_MASK; |
| } |
| |
| /* |
| * For memory tiering mode, too high promotion/demotion throughput may |
| * hurt application latency. So we provide a mechanism to rate limit |
| * the number of pages that are tried to be promoted. |
| */ |
| static bool numa_promotion_rate_limit(struct pglist_data *pgdat, |
| unsigned long rate_limit, int nr) |
| { |
| unsigned long nr_cand; |
| unsigned int now, start; |
| |
| now = jiffies_to_msecs(jiffies); |
| mod_node_page_state(pgdat, PGPROMOTE_CANDIDATE, nr); |
| nr_cand = node_page_state(pgdat, PGPROMOTE_CANDIDATE); |
| start = pgdat->nbp_rl_start; |
| if (now - start > MSEC_PER_SEC && |
| cmpxchg(&pgdat->nbp_rl_start, start, now) == start) |
| pgdat->nbp_rl_nr_cand = nr_cand; |
| if (nr_cand - pgdat->nbp_rl_nr_cand >= rate_limit) |
| return true; |
| return false; |
| } |
| |
| #define NUMA_MIGRATION_ADJUST_STEPS 16 |
| |
| static void numa_promotion_adjust_threshold(struct pglist_data *pgdat, |
| unsigned long rate_limit, |
| unsigned int ref_th) |
| { |
| unsigned int now, start, th_period, unit_th, th; |
| unsigned long nr_cand, ref_cand, diff_cand; |
| |
| now = jiffies_to_msecs(jiffies); |
| th_period = sysctl_numa_balancing_scan_period_max; |
| start = pgdat->nbp_th_start; |
| if (now - start > th_period && |
| cmpxchg(&pgdat->nbp_th_start, start, now) == start) { |
| ref_cand = rate_limit * |
| sysctl_numa_balancing_scan_period_max / MSEC_PER_SEC; |
| nr_cand = node_page_state(pgdat, PGPROMOTE_CANDIDATE); |
| diff_cand = nr_cand - pgdat->nbp_th_nr_cand; |
| unit_th = ref_th * 2 / NUMA_MIGRATION_ADJUST_STEPS; |
| th = pgdat->nbp_threshold ? : ref_th; |
| if (diff_cand > ref_cand * 11 / 10) |
| th = max(th - unit_th, unit_th); |
| else if (diff_cand < ref_cand * 9 / 10) |
| th = min(th + unit_th, ref_th * 2); |
| pgdat->nbp_th_nr_cand = nr_cand; |
| pgdat->nbp_threshold = th; |
| } |
| } |
| |
| bool should_numa_migrate_memory(struct task_struct *p, struct folio *folio, |
| int src_nid, int dst_cpu) |
| { |
| struct numa_group *ng = deref_curr_numa_group(p); |
| int dst_nid = cpu_to_node(dst_cpu); |
| int last_cpupid, this_cpupid; |
| |
| /* |
| * Cannot migrate to memoryless nodes. |
| */ |
| if (!node_state(dst_nid, N_MEMORY)) |
| return false; |
| |
| /* |
| * The pages in slow memory node should be migrated according |
| * to hot/cold instead of private/shared. |
| */ |
| if (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING && |
| !node_is_toptier(src_nid)) { |
| struct pglist_data *pgdat; |
| unsigned long rate_limit; |
| unsigned int latency, th, def_th; |
| |
| pgdat = NODE_DATA(dst_nid); |
| if (pgdat_free_space_enough(pgdat)) { |
| /* workload changed, reset hot threshold */ |
| pgdat->nbp_threshold = 0; |
| return true; |
| } |
| |
| def_th = sysctl_numa_balancing_hot_threshold; |
| rate_limit = sysctl_numa_balancing_promote_rate_limit << \ |
| (20 - PAGE_SHIFT); |
| numa_promotion_adjust_threshold(pgdat, rate_limit, def_th); |
| |
| th = pgdat->nbp_threshold ? : def_th; |
| latency = numa_hint_fault_latency(folio); |
| if (latency >= th) |
| return false; |
| |
| return !numa_promotion_rate_limit(pgdat, rate_limit, |
| folio_nr_pages(folio)); |
| } |
| |
| this_cpupid = cpu_pid_to_cpupid(dst_cpu, current->pid); |
| last_cpupid = folio_xchg_last_cpupid(folio, this_cpupid); |
| |
| if (!(sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING) && |
| !node_is_toptier(src_nid) && !cpupid_valid(last_cpupid)) |
| return false; |
| |
| /* |
| * Allow first faults or private faults to migrate immediately early in |
| * the lifetime of a task. The magic number 4 is based on waiting for |
| * two full passes of the "multi-stage node selection" test that is |
| * executed below. |
| */ |
| if ((p->numa_preferred_nid == NUMA_NO_NODE || p->numa_scan_seq <= 4) && |
| (cpupid_pid_unset(last_cpupid) || cpupid_match_pid(p, last_cpupid))) |
| return true; |
| |
| /* |
| * Multi-stage node selection is used in conjunction with a periodic |
| * migration fault to build a temporal task<->page relation. By using |
| * a two-stage filter we remove short/unlikely relations. |
| * |
| * Using P(p) ~ n_p / n_t as per frequentist probability, we can equate |
| * a task's usage of a particular page (n_p) per total usage of this |
| * page (n_t) (in a given time-span) to a probability. |
| * |
| * Our periodic faults will sample this probability and getting the |
| * same result twice in a row, given these samples are fully |
| * independent, is then given by P(n)^2, provided our sample period |
| * is sufficiently short compared to the usage pattern. |
| * |
| * This quadric squishes small probabilities, making it less likely we |
| * act on an unlikely task<->page relation. |
| */ |
| if (!cpupid_pid_unset(last_cpupid) && |
| cpupid_to_nid(last_cpupid) != dst_nid) |
| return false; |
| |
| /* Always allow migrate on private faults */ |
| if (cpupid_match_pid(p, last_cpupid)) |
| return true; |
| |
| /* A shared fault, but p->numa_group has not been set up yet. */ |
| if (!ng) |
| return true; |
| |
| /* |
| * Destination node is much more heavily used than the source |
| * node? Allow migration. |
| */ |
| if (group_faults_cpu(ng, dst_nid) > group_faults_cpu(ng, src_nid) * |
| ACTIVE_NODE_FRACTION) |
| return true; |
| |
| /* |
| * Distribute memory according to CPU & memory use on each node, |
| * with 3/4 hysteresis to avoid unnecessary memory migrations: |
| * |
| * faults_cpu(dst) 3 faults_cpu(src) |
| * --------------- * - > --------------- |
| * faults_mem(dst) 4 faults_mem(src) |
| */ |
| return group_faults_cpu(ng, dst_nid) * group_faults(p, src_nid) * 3 > |
| group_faults_cpu(ng, src_nid) * group_faults(p, dst_nid) * 4; |
| } |
| |
| /* |
| * 'numa_type' describes the node at the moment of load balancing. |
| */ |
| enum numa_type { |
| /* The node has spare capacity that can be used to run more tasks. */ |
| node_has_spare = 0, |
| /* |
| * The node is fully used and the tasks don't compete for more CPU |
| * cycles. Nevertheless, some tasks might wait before running. |
| */ |
| node_fully_busy, |
| /* |
| * The node is overloaded and can't provide expected CPU cycles to all |
| * tasks. |
| */ |
| node_overloaded |
| }; |
| |
| /* Cached statistics for all CPUs within a node */ |
| struct numa_stats { |
| unsigned long load; |
| unsigned long runnable; |
| unsigned long util; |
| /* Total compute capacity of CPUs on a node */ |
| unsigned long compute_capacity; |
| unsigned int nr_running; |
| unsigned int weight; |
| enum numa_type node_type; |
| int idle_cpu; |
| }; |
| |
| struct task_numa_env { |
| struct task_struct *p; |
| |
| int src_cpu, src_nid; |
| int dst_cpu, dst_nid; |
| int imb_numa_nr; |
| |
| struct numa_stats src_stats, dst_stats; |
| |
| int imbalance_pct; |
| int dist; |
| |
| struct task_struct *best_task; |
| long best_imp; |
| int best_cpu; |
| }; |
| |
| static unsigned long cpu_load(struct rq *rq); |
| static unsigned long cpu_runnable(struct rq *rq); |
| |
| static inline enum |
| numa_type numa_classify(unsigned int imbalance_pct, |
| struct numa_stats *ns) |
| { |
| if ((ns->nr_running > ns->weight) && |
| (((ns->compute_capacity * 100) < (ns->util * imbalance_pct)) || |
| ((ns->compute_capacity * imbalance_pct) < (ns->runnable * 100)))) |
| return node_overloaded; |
| |
| if ((ns->nr_running < ns->weight) || |
| (((ns->compute_capacity * 100) > (ns->util * imbalance_pct)) && |
| ((ns->compute_capacity * imbalance_pct) > (ns->runnable * 100)))) |
| return node_has_spare; |
| |
| return node_fully_busy; |
| } |
| |
| #ifdef CONFIG_SCHED_SMT |
| /* Forward declarations of select_idle_sibling helpers */ |
| static inline bool test_idle_cores(int cpu); |
| static inline int numa_idle_core(int idle_core, int cpu) |
| { |
| if (!static_branch_likely(&sched_smt_present) || |
| idle_core >= 0 || !test_idle_cores(cpu)) |
| return idle_core; |
| |
| /* |
| * Prefer cores instead of packing HT siblings |
| * and triggering future load balancing. |
| */ |
| if (is_core_idle(cpu)) |
| idle_core = cpu; |
| |
| return idle_core; |
| } |
| #else |
| static inline int numa_idle_core(int idle_core, int cpu) |
| { |
| return idle_core; |
| } |
| #endif |
| |
| /* |
| * Gather all necessary information to make NUMA balancing placement |
| * decisions that are compatible with standard load balancer. This |
| * borrows code and logic from update_sg_lb_stats but sharing a |
| * common implementation is impractical. |
| */ |
| static void update_numa_stats(struct task_numa_env *env, |
| struct numa_stats *ns, int nid, |
| bool find_idle) |
| { |
| int cpu, idle_core = -1; |
| |
| memset(ns, 0, sizeof(*ns)); |
| ns->idle_cpu = -1; |
| |
| rcu_read_lock(); |
| for_each_cpu(cpu, cpumask_of_node(nid)) { |
| struct rq *rq = cpu_rq(cpu); |
| |
| ns->load += cpu_load(rq); |
| ns->runnable += cpu_runnable(rq); |
| ns->util += cpu_util_cfs(cpu); |
| ns->nr_running += rq->cfs.h_nr_running; |
| ns->compute_capacity += capacity_of(cpu); |
| |
| if (find_idle && idle_core < 0 && !rq->nr_running && idle_cpu(cpu)) { |
| if (READ_ONCE(rq->numa_migrate_on) || |
| !cpumask_test_cpu(cpu, env->p->cpus_ptr)) |
| continue; |
| |
| if (ns->idle_cpu == -1) |
| ns->idle_cpu = cpu; |
| |
| idle_core = numa_idle_core(idle_core, cpu); |
| } |
| } |
| rcu_read_unlock(); |
| |
| ns->weight = cpumask_weight(cpumask_of_node(nid)); |
| |
| ns->node_type = numa_classify(env->imbalance_pct, ns); |
| |
| if (idle_core >= 0) |
| ns->idle_cpu = idle_core; |
| } |
| |
| static void task_numa_assign(struct task_numa_env *env, |
| struct task_struct *p, long imp) |
| { |
| struct rq *rq = cpu_rq(env->dst_cpu); |
| |
| /* Check if run-queue part of active NUMA balance. */ |
| if (env->best_cpu != env->dst_cpu && xchg(&rq->numa_migrate_on, 1)) { |
| int cpu; |
| int start = env->dst_cpu; |
| |
| /* Find alternative idle CPU. */ |
| for_each_cpu_wrap(cpu, cpumask_of_node(env->dst_nid), start + 1) { |
| if (cpu == env->best_cpu || !idle_cpu(cpu) || |
| !cpumask_test_cpu(cpu, env->p->cpus_ptr)) { |
| continue; |
| } |
| |
| env->dst_cpu = cpu; |
| rq = cpu_rq(env->dst_cpu); |
| if (!xchg(&rq->numa_migrate_on, 1)) |
| goto assign; |
| } |
| |
| /* Failed to find an alternative idle CPU */ |
| return; |
| } |
| |
| assign: |
| /* |
| * Clear previous best_cpu/rq numa-migrate flag, since task now |
| * found a better CPU to move/swap. |
| */ |
| if (env->best_cpu != -1 && env->best_cpu != env->dst_cpu) { |
| rq = cpu_rq(env->best_cpu); |
| WRITE_ONCE(rq->numa_migrate_on, 0); |
| } |
| |
| if (env->best_task) |
| put_task_struct(env->best_task); |
| if (p) |
| get_task_struct(p); |
| |
| env->best_task = p; |
| env->best_imp = imp; |
| env->best_cpu = env->dst_cpu; |
| } |
| |
| static bool load_too_imbalanced(long src_load, long dst_load, |
| struct task_numa_env *env) |
| { |
| long imb, old_imb; |
| long orig_src_load, orig_dst_load; |
| long src_capacity, dst_capacity; |
| |
| /* |
| * The load is corrected for the CPU capacity available on each node. |
| * |
| * src_load dst_load |
| * ------------ vs --------- |
| * src_capacity dst_capacity |
| */ |
| src_capacity = env->src_stats.compute_capacity; |
| dst_capacity = env->dst_stats.compute_capacity; |
| |
| imb = abs(dst_load * src_capacity - src_load * dst_capacity); |
| |
| orig_src_load = env->src_stats.load; |
| orig_dst_load = env->dst_stats.load; |
| |
| old_imb = abs(orig_dst_load * src_capacity - orig_src_load * dst_capacity); |
| |
| /* Would this change make things worse? */ |
| return (imb > old_imb); |
| } |
| |
| /* |
| * Maximum NUMA importance can be 1998 (2*999); |
| * SMALLIMP @ 30 would be close to 1998/64. |
| * Used to deter task migration. |
| */ |
| #define SMALLIMP 30 |
| |
| /* |
| * This checks if the overall compute and NUMA accesses of the system would |
| * be improved if the source tasks was migrated to the target dst_cpu taking |
| * into account that it might be best if task running on the dst_cpu should |
| * be exchanged with the source task |
| */ |
| static bool task_numa_compare(struct task_numa_env *env, |
| long taskimp, long groupimp, bool maymove) |
| { |
| struct numa_group *cur_ng, *p_ng = deref_curr_numa_group(env->p); |
| struct rq *dst_rq = cpu_rq(env->dst_cpu); |
| long imp = p_ng ? groupimp : taskimp; |
| struct task_struct *cur; |
| long src_load, dst_load; |
| int dist = env->dist; |
| long moveimp = imp; |
| long load; |
| bool stopsearch = false; |
| |
| if (READ_ONCE(dst_rq->numa_migrate_on)) |
| return false; |
| |
| rcu_read_lock(); |
| cur = rcu_dereference(dst_rq->curr); |
| if (cur && ((cur->flags & PF_EXITING) || is_idle_task(cur))) |
| cur = NULL; |
| |
| /* |
| * Because we have preemption enabled we can get migrated around and |
| * end try selecting ourselves (current == env->p) as a swap candidate. |
| */ |
| if (cur == env->p) { |
| stopsearch = true; |
| goto unlock; |
| } |
| |
| if (!cur) { |
| if (maymove && moveimp >= env->best_imp) |
| goto assign; |
| else |
| goto unlock; |
| } |
| |
| /* Skip this swap candidate if cannot move to the source cpu. */ |
| if (!cpumask_test_cpu(env->src_cpu, cur->cpus_ptr)) |
| goto unlock; |
| |
| /* |
| * Skip this swap candidate if it is not moving to its preferred |
| * node and the best task is. |
| */ |
| if (env->best_task && |
| env->best_task->numa_preferred_nid == env->src_nid && |
| cur->numa_preferred_nid != env->src_nid) { |
| goto unlock; |
| } |
| |
| /* |
| * "imp" is the fault differential for the source task between the |
| * source and destination node. Calculate the total differential for |
| * the source task and potential destination task. The more negative |
| * the value is, the more remote accesses that would be expected to |
| * be incurred if the tasks were swapped. |
| * |
| * If dst and source tasks are in the same NUMA group, or not |
| * in any group then look only at task weights. |
| */ |
| cur_ng = rcu_dereference(cur->numa_group); |
| if (cur_ng == p_ng) { |
| /* |
| * Do not swap within a group or between tasks that have |
| * no group if there is spare capacity. Swapping does |
| * not address the load imbalance and helps one task at |
| * the cost of punishing another. |
| */ |
| if (env->dst_stats.node_type == node_has_spare) |
| goto unlock; |
| |
| imp = taskimp + task_weight(cur, env->src_nid, dist) - |
| task_weight(cur, env->dst_nid, dist); |
| /* |
| * Add some hysteresis to prevent swapping the |
| * tasks within a group over tiny differences. |
| */ |
| if (cur_ng) |
| imp -= imp / 16; |
| } else { |
| /* |
| * Compare the group weights. If a task is all by itself |
| * (not part of a group), use the task weight instead. |
| */ |
| if (cur_ng && p_ng) |
| imp += group_weight(cur, env->src_nid, dist) - |
| group_weight(cur, env->dst_nid, dist); |
| else |
| imp += task_weight(cur, env->src_nid, dist) - |
| task_weight(cur, env->dst_nid, dist); |
| } |
| |
| /* Discourage picking a task already on its preferred node */ |
| if (cur->numa_preferred_nid == env->dst_nid) |
| imp -= imp / 16; |
| |
| /* |
| * Encourage picking a task that moves to its preferred node. |
| * This potentially makes imp larger than it's maximum of |
| * 1998 (see SMALLIMP and task_weight for why) but in this |
| * case, it does not matter. |
| */ |
| if (cur->numa_preferred_nid == env->src_nid) |
| imp += imp / 8; |
| |
| if (maymove && moveimp > imp && moveimp > env->best_imp) { |
| imp = moveimp; |
| cur = NULL; |
| goto assign; |
| } |
| |
| /* |
| * Prefer swapping with a task moving to its preferred node over a |
| * task that is not. |
| */ |
| if (env->best_task && cur->numa_preferred_nid == env->src_nid && |
| env->best_task->numa_preferred_nid != env->src_nid) { |
| goto assign; |
| } |
| |
| /* |
| * If the NUMA importance is less than SMALLIMP, |
| * task migration might only result in ping pong |
| * of tasks and also hurt performance due to cache |
| * misses. |
| */ |
| if (imp < SMALLIMP || imp <= env->best_imp + SMALLIMP / 2) |
| goto unlock; |
| |
| /* |
| * In the overloaded case, try and keep the load balanced. |
| */ |
| load = task_h_load(env->p) - task_h_load(cur); |
| if (!load) |
| goto assign; |
| |
| dst_load = env->dst_stats.load + load; |
| src_load = env->src_stats.load - load; |
| |
| if (load_too_imbalanced(src_load, dst_load, env)) |
| goto unlock; |
| |
| assign: |
| /* Evaluate an idle CPU for a task numa move. */ |
| if (!cur) { |
| int cpu = env->dst_stats.idle_cpu; |
| |
| /* Nothing cached so current CPU went idle since the search. */ |
| if (cpu < 0) |
| cpu = env->dst_cpu; |
| |
| /* |
| * If the CPU is no longer truly idle and the previous best CPU |
| * is, keep using it. |
| */ |
| if (!idle_cpu(cpu) && env->best_cpu >= 0 && |
| idle_cpu(env->best_cpu)) { |
| cpu = env->best_cpu; |
| } |
| |
| env->dst_cpu = cpu; |
| } |
| |
| task_numa_assign(env, cur, imp); |
| |
| /* |
| * If a move to idle is allowed because there is capacity or load |
| * balance improves then stop the search. While a better swap |
| * candidate may exist, a search is not free. |
| */ |
| if (maymove && !cur && env->best_cpu >= 0 && idle_cpu(env->best_cpu)) |
| stopsearch = true; |
| |
| /* |
| * If a swap candidate must be identified and the current best task |
| * moves its preferred node then stop the search. |
| */ |
| if (!maymove && env->best_task && |
| env->best_task->numa_preferred_nid == env->src_nid) { |
| stopsearch = true; |
| } |
| unlock: |
| rcu_read_unlock(); |
| |
| return stopsearch; |
| } |
| |
| static void task_numa_find_cpu(struct task_numa_env *env, |
| long taskimp, long groupimp) |
| { |
| bool maymove = false; |
| int cpu; |
| |
| /* |
| * If dst node has spare capacity, then check if there is an |
| * imbalance that would be overruled by the load balancer. |
| */ |
| if (env->dst_stats.node_type == node_has_spare) { |
| unsigned int imbalance; |
| int src_running, dst_running; |
| |
| /* |
| * Would movement cause an imbalance? Note that if src has |
| * more running tasks that the imbalance is ignored as the |
| * move improves the imbalance from the perspective of the |
| * CPU load balancer. |
| * */ |
| src_running = env->src_stats.nr_running - 1; |
| dst_running = env->dst_stats.nr_running + 1; |
| imbalance = max(0, dst_running - src_running); |
| imbalance = adjust_numa_imbalance(imbalance, dst_running, |
| env->imb_numa_nr); |
| |
| /* Use idle CPU if there is no imbalance */ |
| if (!imbalance) { |
| maymove = true; |
| if (env->dst_stats.idle_cpu >= 0) { |
| env->dst_cpu = env->dst_stats.idle_cpu; |
| task_numa_assign(env, NULL, 0); |
| return; |
| } |
| } |
| } else { |
| long src_load, dst_load, load; |
| /* |
| * If the improvement from just moving env->p direction is better |
| * than swapping tasks around, check if a move is possible. |
| */ |
| load = task_h_load(env->p); |
| dst_load = env->dst_stats.load + load; |
| src_load = env->src_stats.load - load; |
| maymove = !load_too_imbalanced(src_load, dst_load, env); |
| } |
| |
| for_each_cpu(cpu, cpumask_of_node(env->dst_nid)) { |
| /* Skip this CPU if the source task cannot migrate */ |
| if (!cpumask_test_cpu(cpu, env->p->cpus_ptr)) |
| continue; |
| |
| env->dst_cpu = cpu; |
| if (task_numa_compare(env, taskimp, groupimp, maymove)) |
| break; |
| } |
| } |
| |
| static int task_numa_migrate(struct task_struct *p) |
| { |
| struct task_numa_env env = { |
| .p = p, |
| |
| .src_cpu = task_cpu(p), |
| .src_nid = task_node(p), |
| |
| .imbalance_pct = 112, |
| |
| .best_task = NULL, |
| .best_imp = 0, |
| .best_cpu = -1, |
| }; |
| unsigned long taskweight, groupweight; |
| struct sched_domain *sd; |
| long taskimp, groupimp; |
| struct numa_group *ng; |
| struct rq *best_rq; |
| int nid, ret, dist; |
| |
| /* |
| * Pick the lowest SD_NUMA domain, as that would have the smallest |
| * imbalance and would be the first to start moving tasks about. |
| * |
| * And we want to avoid any moving of tasks about, as that would create |
| * random movement of tasks -- counter the numa conditions we're trying |
| * to satisfy here. |
| */ |
| rcu_read_lock(); |
| sd = rcu_dereference(per_cpu(sd_numa, env.src_cpu)); |
| if (sd) { |
| env.imbalance_pct = 100 + (sd->imbalance_pct - 100) / 2; |
| env.imb_numa_nr = sd->imb_numa_nr; |
| } |
| rcu_read_unlock(); |
| |
| /* |
| * Cpusets can break the scheduler domain tree into smaller |
| * balance domains, some of which do not cross NUMA boundaries. |
| * Tasks that are "trapped" in such domains cannot be migrated |
| * elsewhere, so there is no point in (re)trying. |
| */ |
| if (unlikely(!sd)) { |
| sched_setnuma(p, task_node(p)); |
| return -EINVAL; |
| } |
| |
| env.dst_nid = p->numa_preferred_nid; |
| dist = env.dist = node_distance(env.src_nid, env.dst_nid); |
| taskweight = task_weight(p, env.src_nid, dist); |
| groupweight = group_weight(p, env.src_nid, dist); |
| update_numa_stats(&env, &env.src_stats, env.src_nid, false); |
| taskimp = task_weight(p, env.dst_nid, dist) - taskweight; |
| groupimp = group_weight(p, env.dst_nid, dist) - groupweight; |
| update_numa_stats(&env, &env.dst_stats, env.dst_nid, true); |
| |
| /* Try to find a spot on the preferred nid. */ |
| task_numa_find_cpu(&env, taskimp, groupimp); |
| |
| /* |
| * Look at other nodes in these cases: |
| * - there is no space available on the preferred_nid |
| * - the task is part of a numa_group that is interleaved across |
| * multiple NUMA nodes; in order to better consolidate the group, |
| * we need to check other locations. |
| */ |
| ng = deref_curr_numa_group(p); |
| if (env.best_cpu == -1 || (ng && ng->active_nodes > 1)) { |
| for_each_node_state(nid, N_CPU) { |
| if (nid == env.src_nid || nid == p->numa_preferred_nid) |
| continue; |
| |
| dist = node_distance(env.src_nid, env.dst_nid); |
| if (sched_numa_topology_type == NUMA_BACKPLANE && |
| dist != env.dist) { |
| taskweight = task_weight(p, env.src_nid, dist); |
| groupweight = group_weight(p, env.src_nid, dist); |
| } |
| |
| /* Only consider nodes where both task and groups benefit */ |
| taskimp = task_weight(p, nid, dist) - taskweight; |
| groupimp = group_weight(p, nid, dist) - groupweight; |
| if (taskimp < 0 && groupimp < 0) |
| continue; |
| |
| env.dist = dist; |
| env.dst_nid = nid; |
| update_numa_stats(&env, &env.dst_stats, env.dst_nid, true); |
| task_numa_find_cpu(&env, taskimp, groupimp); |
| } |
| } |
| |
| /* |
| * If the task is part of a workload that spans multiple NUMA nodes, |
| * and is migrating into one of the workload's active nodes, remember |
| * this node as the task's preferred numa node, so the workload can |
| * settle down. |
| * A task that migrated to a second choice node will be better off |
| * trying for a better one later. Do not set the preferred node here. |
| */ |
| if (ng) { |
| if (env.best_cpu == -1) |
| nid = env.src_nid; |
| else |
| nid = cpu_to_node(env.best_cpu); |
| |
| if (nid != p->numa_preferred_nid) |
| sched_setnuma(p, nid); |
| } |
| |
| /* No better CPU than the current one was found. */ |
| if (env.best_cpu == -1) { |
| trace_sched_stick_numa(p, env.src_cpu, NULL, -1); |
| return -EAGAIN; |
| } |
| |
| best_rq = cpu_rq(env.best_cpu); |
| if (env.best_task == NULL) { |
| ret = migrate_task_to(p, env.best_cpu); |
| WRITE_ONCE(best_rq->numa_migrate_on, 0); |
| if (ret != 0) |
| trace_sched_stick_numa(p, env.src_cpu, NULL, env.best_cpu); |
| return ret; |
| } |
| |
| ret = migrate_swap(p, env.best_task, env.best_cpu, env.src_cpu); |
| WRITE_ONCE(best_rq->numa_migrate_on, 0); |
| |
| if (ret != 0) |
| trace_sched_stick_numa(p, env.src_cpu, env.best_task, env.best_cpu); |
| put_task_struct(env.best_task); |
| return ret; |
| } |
| |
| /* Attempt to migrate a task to a CPU on the preferred node. */ |
| static void numa_migrate_preferred(struct task_struct *p) |
| { |
| unsigned long interval = HZ; |
| |
| /* This task has no NUMA fault statistics yet */ |
| if (unlikely(p->numa_preferred_nid == NUMA_NO_NODE || !p->numa_faults)) |
| return; |
| |
| /* Periodically retry migrating the task to the preferred node */ |
| interval = min(interval, msecs_to_jiffies(p->numa_scan_period) / 16); |
| p->numa_migrate_retry = jiffies + interval; |
| |
| /* Success if task is already running on preferred CPU */ |
| if (task_node(p) == p->numa_preferred_nid) |
| return; |
| |
| /* Otherwise, try migrate to a CPU on the preferred node */ |
| task_numa_migrate(p); |
| } |
| |
| /* |
| * Find out how many nodes the workload is actively running on. Do this by |
| * tracking the nodes from which NUMA hinting faults are triggered. This can |
| * be different from the set of nodes where the workload's memory is currently |
| * located. |
| */ |
| static void numa_group_count_active_nodes(struct numa_group *numa_group) |
| { |
| unsigned long faults, max_faults = 0; |
| int nid, active_nodes = 0; |
| |
| for_each_node_state(nid, N_CPU) { |
| faults = group_faults_cpu(numa_group, nid); |
| if (faults > max_faults) |
| max_faults = faults; |
| } |
| |
| for_each_node_state(nid, N_CPU) { |
| faults = group_faults_cpu(numa_group, nid); |
| if (faults * ACTIVE_NODE_FRACTION > max_faults) |
| active_nodes++; |
| } |
| |
| numa_group->max_faults_cpu = max_faults; |
| numa_group->active_nodes = active_nodes; |
| } |
| |
| /* |
| * When adapting the scan rate, the period is divided into NUMA_PERIOD_SLOTS |
| * increments. The more local the fault statistics are, the higher the scan |
| * period will be for the next scan window. If local/(local+remote) ratio is |
| * below NUMA_PERIOD_THRESHOLD (where range of ratio is 1..NUMA_PERIOD_SLOTS) |
| * the scan period will decrease. Aim for 70% local accesses. |
| */ |
| #define NUMA_PERIOD_SLOTS 10 |
| #define NUMA_PERIOD_THRESHOLD 7 |
| |
| /* |
| * Increase the scan period (slow down scanning) if the majority of |
| * our memory is already on our local node, or if the majority of |
| * the page accesses are shared with other processes. |
| * Otherwise, decrease the scan period. |
| */ |
| static void update_task_scan_period(struct task_struct *p, |
| unsigned long shared, unsigned long private) |
| { |
| unsigned int period_slot; |
| int lr_ratio, ps_ratio; |
| int diff; |
| |
| unsigned long remote = p->numa_faults_locality[0]; |
| unsigned long local = p->numa_faults_locality[1]; |
| |
| /* |
| * If there were no record hinting faults then either the task is |
| * completely idle or all activity is in areas that are not of interest |
| * to automatic numa balancing. Related to that, if there were failed |
| * migration then it implies we are migrating too quickly or the local |
| * node is overloaded. In either case, scan slower |
| */ |
| if (local + shared == 0 || p->numa_faults_locality[2]) { |
| p->numa_scan_period = min(p->numa_scan_period_max, |
| p->numa_scan_period << 1); |
| |
| p->mm->numa_next_scan = jiffies + |
| msecs_to_jiffies(p->numa_scan_period); |
| |
| return; |
| } |
| |
| /* |
| * Prepare to scale scan period relative to the current period. |
| * == NUMA_PERIOD_THRESHOLD scan period stays the same |
| * < NUMA_PERIOD_THRESHOLD scan period decreases (scan faster) |
| * >= NUMA_PERIOD_THRESHOLD scan period increases (scan slower) |
| */ |
| period_slot = DIV_ROUND_UP(p->numa_scan_period, NUMA_PERIOD_SLOTS); |
| lr_ratio = (local * NUMA_PERIOD_SLOTS) / (local + remote); |
| ps_ratio = (private * NUMA_PERIOD_SLOTS) / (private + shared); |
| |
| if (ps_ratio >= NUMA_PERIOD_THRESHOLD) { |
| /* |
| * Most memory accesses are local. There is no need to |
| * do fast NUMA scanning, since memory is already local. |
| */ |
| int slot = ps_ratio - NUMA_PERIOD_THRESHOLD; |
| if (!slot) |
| slot = 1; |
| diff = slot * period_slot; |
| } else if (lr_ratio >= NUMA_PERIOD_THRESHOLD) { |
| /* |
| * Most memory accesses are shared with other tasks. |
| * There is no point in continuing fast NUMA scanning, |
| * since other tasks may just move the memory elsewhere. |
| */ |
| int slot = lr_ratio - NUMA_PERIOD_THRESHOLD; |
| if (!slot) |
| slot = 1; |
| diff = slot * period_slot; |
| } else { |
| /* |
| * Private memory faults exceed (SLOTS-THRESHOLD)/SLOTS, |
| * yet they are not on the local NUMA node. Speed up |
| * NUMA scanning to get the memory moved over. |
| */ |
| int ratio = max(lr_ratio, ps_ratio); |
| diff = -(NUMA_PERIOD_THRESHOLD - ratio) * period_slot; |
| } |
| |
| p->numa_scan_period = clamp(p->numa_scan_period + diff, |
| task_scan_min(p), task_scan_max(p)); |
| memset(p->numa_faults_locality, 0, sizeof(p->numa_faults_locality)); |
| } |
| |
| /* |
| * Get the fraction of time the task has been running since the last |
| * NUMA placement cycle. The scheduler keeps similar statistics, but |
| * decays those on a 32ms period, which is orders of magnitude off |
| * from the dozens-of-seconds NUMA balancing period. Use the scheduler |
| * stats only if the task is so new there are no NUMA statistics yet. |
| */ |
| static u64 numa_get_avg_runtime(struct task_struct *p, u64 *period) |
| { |
| u64 runtime, delta, now; |
| /* Use the start of this time slice to avoid calculations. */ |
| now = p->se.exec_start; |
| runtime = p->se.sum_exec_runtime; |
| |
| if (p->last_task_numa_placement) { |
| delta = runtime - p->last_sum_exec_runtime; |
| *period = now - p->last_task_numa_placement; |
| |
| /* Avoid time going backwards, prevent potential divide error: */ |
| if (unlikely((s64)*period < 0)) |
| *period = 0; |
| } else { |
| delta = p->se.avg.load_sum; |
| *period = LOAD_AVG_MAX; |
| } |
| |
| p->last_sum_exec_runtime = runtime; |
| p->last_task_numa_placement = now; |
| |
| return delta; |
| } |
| |
| /* |
| * Determine the preferred nid for a task in a numa_group. This needs to |
| * be done in a way that produces consistent results with group_weight, |
| * otherwise workloads might not converge. |
| */ |
| static int preferred_group_nid(struct task_struct *p, int nid) |
| { |
| nodemask_t nodes; |
| int dist; |
| |
| /* Direct connections between all NUMA nodes. */ |
| if (sched_numa_topology_type == NUMA_DIRECT) |
| return nid; |
| |
| /* |
| * On a system with glueless mesh NUMA topology, group_weight |
| * scores nodes according to the number of NUMA hinting faults on |
| * both the node itself, and on nearby nodes. |
| */ |
| if (sched_numa_topology_type == NUMA_GLUELESS_MESH) { |
| unsigned long score, max_score = 0; |
| int node, max_node = nid; |
| |
| dist = sched_max_numa_distance; |
| |
| for_each_node_state(node, N_CPU) { |
| score = group_weight(p, node, dist); |
| if (score > max_score) { |
| max_score = score; |
| max_node = node; |
| } |
| } |
| return max_node; |
| } |
| |
| /* |
| * Finding the preferred nid in a system with NUMA backplane |
| * interconnect topology is more involved. The goal is to locate |
| * tasks from numa_groups near each other in the system, and |
| * untangle workloads from different sides of the system. This requires |
| * searching down the hierarchy of node groups, recursively searching |
| * inside the highest scoring group of nodes. The nodemask tricks |
| * keep the complexity of the search down. |
| */ |
| nodes = node_states[N_CPU]; |
| for (dist = sched_max_numa_distance; dist > LOCAL_DISTANCE; dist--) { |
| unsigned long max_faults = 0; |
| nodemask_t max_group = NODE_MASK_NONE; |
| int a, b; |
| |
| /* Are there nodes at this distance from each other? */ |
| if (!find_numa_distance(dist)) |
| continue; |
| |
| for_each_node_mask(a, nodes) { |
| unsigned long faults = 0; |
| nodemask_t this_group; |
| nodes_clear(this_group); |
| |
| /* Sum group's NUMA faults; includes a==b case. */ |
| for_each_node_mask(b, nodes) { |
| if (node_distance(a, b) < dist) { |
| faults += group_faults(p, b); |
| node_set(b, this_group); |
| node_clear(b, nodes); |
| } |
| } |
| |
| /* Remember the top group. */ |
| if (faults > max_faults) { |
| max_faults = faults; |
| max_group = this_group; |
| /* |
| * subtle: at the smallest distance there is |
| * just one node left in each "group", the |
| * winner is the preferred nid. |
| */ |
| nid = a; |
| } |
| } |
| /* Next round, evaluate the nodes within max_group. */ |
| if (!max_faults) |
| break; |
| nodes = max_group; |
| } |
| return nid; |
| } |
| |
| static void task_numa_placement(struct task_struct *p) |
| { |
| int seq, nid, max_nid = NUMA_NO_NODE; |
| unsigned long max_faults = 0; |
| unsigned long fault_types[2] = { 0, 0 }; |
| unsigned long total_faults; |
| u64 runtime, period; |
| spinlock_t *group_lock = NULL; |
| struct numa_group *ng; |
| |
| /* |
| * The p->mm->numa_scan_seq field gets updated without |
| * exclusive access. Use READ_ONCE() here to ensure |
| * that the field is read in a single access: |
| */ |
| seq = READ_ONCE(p->mm->numa_scan_seq); |
| if (p->numa_scan_seq == seq) |
| return; |
| p->numa_scan_seq = seq; |
| p->numa_scan_period_max = task_scan_max(p); |
| |
| total_faults = p->numa_faults_locality[0] + |
| p->numa_faults_locality[1]; |
| runtime = numa_get_avg_runtime(p, &period); |
| |
| /* If the task is part of a group prevent parallel updates to group stats */ |
| ng = deref_curr_numa_group(p); |
| if (ng) { |
| group_lock = &ng->lock; |
| spin_lock_irq(group_lock); |
| } |
| |
| /* Find the node with the highest number of faults */ |
| for_each_online_node(nid) { |
| /* Keep track of the offsets in numa_faults array */ |
| int mem_idx, membuf_idx, cpu_idx, cpubuf_idx; |
| unsigned long faults = 0, group_faults = 0; |
| int priv; |
| |
| for (priv = 0; priv < NR_NUMA_HINT_FAULT_TYPES; priv++) { |
| long diff, f_diff, f_weight; |
| |
| mem_idx = task_faults_idx(NUMA_MEM, nid, priv); |
| membuf_idx = task_faults_idx(NUMA_MEMBUF, nid, priv); |
| cpu_idx = task_faults_idx(NUMA_CPU, nid, priv); |
| cpubuf_idx = task_faults_idx(NUMA_CPUBUF, nid, priv); |
| |
| /* Decay existing window, copy faults since last scan */ |
| diff = p->numa_faults[membuf_idx] - p->numa_faults[mem_idx] / 2; |
| fault_types[priv] += p->numa_faults[membuf_idx]; |
| p->numa_faults[membuf_idx] = 0; |
| |
| /* |
| * Normalize the faults_from, so all tasks in a group |
| * count according to CPU use, instead of by the raw |
| * number of faults. Tasks with little runtime have |
| * little over-all impact on throughput, and thus their |
| * faults are less important. |
| */ |
| f_weight = div64_u64(runtime << 16, period + 1); |
| f_weight = (f_weight * p->numa_faults[cpubuf_idx]) / |
| (total_faults + 1); |
| f_diff = f_weight - p->numa_faults[cpu_idx] / 2; |
| p->numa_faults[cpubuf_idx] = 0; |
| |
| p->numa_faults[mem_idx] += diff; |
| p->numa_faults[cpu_idx] += f_diff; |
| faults += p->numa_faults[mem_idx]; |
| p->total_numa_faults += diff; |
| if (ng) { |
| /* |
| * safe because we can only change our own group |
| * |
| * mem_idx represents the offset for a given |
| * nid and priv in a specific region because it |
| * is at the beginning of the numa_faults array. |
| */ |
| ng->faults[mem_idx] += diff; |
| ng->faults[cpu_idx] += f_diff; |
| ng->total_faults += diff; |
| group_faults += ng->faults[mem_idx]; |
| } |
| } |
| |
| if (!ng) { |
| if (faults > max_faults) { |
| max_faults = faults; |
| max_nid = nid; |
| } |
| } else if (group_faults > max_faults) { |
| max_faults = group_faults; |
| max_nid = nid; |
| } |
| } |
| |
| /* Cannot migrate task to CPU-less node */ |
| max_nid = numa_nearest_node(max_nid, N_CPU); |
| |
| if (ng) { |
| numa_group_count_active_nodes(ng); |
| spin_unlock_irq(group_lock); |
| max_nid = preferred_group_nid(p, max_nid); |
| } |
| |
| if (max_faults) { |
| /* Set the new preferred node */ |
| if (max_nid != p->numa_preferred_nid) |
| sched_setnuma(p, max_nid); |
| } |
| |
| update_task_scan_period(p, fault_types[0], fault_types[1]); |
| } |
| |
| static inline int get_numa_group(struct numa_group *grp) |
| { |
| return refcount_inc_not_zero(&grp->refcount); |
| } |
| |
| static inline void put_numa_group(struct numa_group *grp) |
| { |
| if (refcount_dec_and_test(&grp->refcount)) |
| kfree_rcu(grp, rcu); |
| } |
| |
| static void task_numa_group(struct task_struct *p, int cpupid, int flags, |
| int *priv) |
| { |
| struct numa_group *grp, *my_grp; |
| struct task_struct *tsk; |
| bool join = false; |
| int cpu = cpupid_to_cpu(cpupid); |
| int i; |
| |
| if (unlikely(!deref_curr_numa_group(p))) { |
| unsigned int size = sizeof(struct numa_group) + |
| NR_NUMA_HINT_FAULT_STATS * |
| nr_node_ids * sizeof(unsigned long); |
| |
| grp = kzalloc(size, GFP_KERNEL | __GFP_NOWARN); |
| if (!grp) |
| return; |
| |
| refcount_set(&grp->refcount, 1); |
| grp->active_nodes = 1; |
| grp->max_faults_cpu = 0; |
| spin_lock_init(&grp->lock); |
| grp->gid = p->pid; |
| |
| for (i = 0; i < NR_NUMA_HINT_FAULT_STATS * nr_node_ids; i++) |
| grp->faults[i] = p->numa_faults[i]; |
| |
| grp->total_faults = p->total_numa_faults; |
| |
| grp->nr_tasks++; |
| rcu_assign_pointer(p->numa_group, grp); |
| } |
| |
| rcu_read_lock(); |
| tsk = READ_ONCE(cpu_rq(cpu)->curr); |
| |
| if (!cpupid_match_pid(tsk, cpupid)) |
| goto no_join; |
| |
| grp = rcu_dereference(tsk->numa_group); |
| if (!grp) |
| goto no_join; |
| |
| my_grp = deref_curr_numa_group(p); |
| if (grp == my_grp) |
| goto no_join; |
| |
| /* |
| * Only join the other group if its bigger; if we're the bigger group, |
| * the other task will join us. |
| */ |
| if (my_grp->nr_tasks > grp->nr_tasks) |
| goto no_join; |
| |
| /* |
| * Tie-break on the grp address. |
| */ |
| if (my_grp->nr_tasks == grp->nr_tasks && my_grp > grp) |
| goto no_join; |
| |
| /* Always join threads in the same process. */ |
| if (tsk->mm == current->mm) |
| join = true; |
| |
| /* Simple filter to avoid false positives due to PID collisions */ |
| if (flags & TNF_SHARED) |
| join = true; |
| |
| /* Update priv based on whether false sharing was detected */ |
| *priv = !join; |
| |
| if (join && !get_numa_group(grp)) |
| goto no_join; |
| |
| rcu_read_unlock(); |
| |
| if (!join) |
| return; |
| |
| WARN_ON_ONCE(irqs_disabled()); |
| double_lock_irq(&my_grp->lock, &grp->lock); |
| |
| for (i = 0; i < NR_NUMA_HINT_FAULT_STATS * nr_node_ids; i++) { |
| my_grp->faults[i] -= p->numa_faults[i]; |
| grp->faults[i] += p->numa_faults[i]; |
| } |
| my_grp->total_faults -= p->total_numa_faults; |
| grp->total_faults += p->total_numa_faults; |
| |
| my_grp->nr_tasks--; |
| grp->nr_tasks++; |
| |
| spin_unlock(&my_grp->lock); |
| spin_unlock_irq(&grp->lock); |
| |
| rcu_assign_pointer(p->numa_group, grp); |
| |
| put_numa_group(my_grp); |
| return; |
| |
| no_join: |
| rcu_read_unlock(); |
| return; |
| } |
| |
| /* |
| * Get rid of NUMA statistics associated with a task (either current or dead). |
| * If @final is set, the task is dead and has reached refcount zero, so we can |
| * safely free all relevant data structures. Otherwise, there might be |
| * concurrent reads from places like load balancing and procfs, and we should |
| * reset the data back to default state without freeing ->numa_faults. |
| */ |
| void task_numa_free(struct task_struct *p, bool final) |
| { |
| /* safe: p either is current or is being freed by current */ |
| struct numa_group *grp = rcu_dereference_raw(p->numa_group); |
| unsigned long *numa_faults = p->numa_faults; |
| unsigned long flags; |
| int i; |
| |
| if (!numa_faults) |
| return; |
| |
| if (grp) { |
| spin_lock_irqsave(&grp->lock, flags); |
| for (i = 0; i < NR_NUMA_HINT_FAULT_STATS * nr_node_ids; i++) |
| grp->faults[i] -= p->numa_faults[i]; |
| grp->total_faults -= p->total_numa_faults; |
| |
| grp->nr_tasks--; |
| spin_unlock_irqrestore(&grp->lock, flags); |
| RCU_INIT_POINTER(p->numa_group, NULL); |
| put_numa_group(grp); |
| } |
| |
| if (final) { |
| p->numa_faults = NULL; |
| kfree(numa_faults); |
| } else { |
| p->total_numa_faults = 0; |
| for (i = 0; i < NR_NUMA_HINT_FAULT_STATS * nr_node_ids; i++) |
| numa_faults[i] = 0; |
| } |
| } |
| |
| /* |
| * Got a PROT_NONE fault for a page on @node. |
| */ |
| void task_numa_fault(int last_cpupid, int mem_node, int pages, int flags) |
| { |
| struct task_struct *p = current; |
| bool migrated = flags & TNF_MIGRATED; |
| int cpu_node = task_node(current); |
| int local = !!(flags & TNF_FAULT_LOCAL); |
| struct numa_group *ng; |
| int priv; |
| |
| if (!static_branch_likely(&sched_numa_balancing)) |
| return; |
| |
| /* for example, ksmd faulting in a user's mm */ |
| if (!p->mm) |
| return; |
| |
| /* |
| * NUMA faults statistics are unnecessary for the slow memory |
| * node for memory tiering mode. |
| */ |
| if (!node_is_toptier(mem_node) && |
| (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING || |
| !cpupid_valid(last_cpupid))) |
| return; |
| |
| /* Allocate buffer to track faults on a per-node basis */ |
| if (unlikely(!p->numa_faults)) { |
| int size = sizeof(*p->numa_faults) * |
| NR_NUMA_HINT_FAULT_BUCKETS * nr_node_ids; |
| |
| p->numa_faults = kzalloc(size, GFP_KERNEL|__GFP_NOWARN); |
| if (!p->numa_faults) |
| return; |
| |
| p->total_numa_faults = 0; |
| memset(p->numa_faults_locality, 0, sizeof(p->numa_faults_locality)); |
| } |
| |
| /* |
| * First accesses are treated as private, otherwise consider accesses |
| * to be private if the accessing pid has not changed |
| */ |
| if (unlikely(last_cpupid == (-1 & LAST_CPUPID_MASK))) { |
| priv = 1; |
| } else { |
| priv = cpupid_match_pid(p, last_cpupid); |
| if (!priv && !(flags & TNF_NO_GROUP)) |
| task_numa_group(p, last_cpupid, flags, &priv); |
| } |
| |
| /* |
| * If a workload spans multiple NUMA nodes, a shared fault that |
| * occurs wholly within the set of nodes that the workload is |
| * actively using should be counted as local. This allows the |
| * scan rate to slow down when a workload has settled down. |
| */ |
| ng = deref_curr_numa_group(p); |
| if (!priv && !local && ng && ng->active_nodes > 1 && |
| numa_is_active_node(cpu_node, ng) && |
| numa_is_active_node(mem_node, ng)) |
| local = 1; |
| |
| /* |
| * Retry to migrate task to preferred node periodically, in case it |
| * previously failed, or the scheduler moved us. |
| */ |
| if (time_after(jiffies, p->numa_migrate_retry)) { |
| task_numa_placement(p); |
| numa_migrate_preferred(p); |
| } |
| |
| if (migrated) |
| p->numa_pages_migrated += pages; |
| if (flags & TNF_MIGRATE_FAIL) |
| p->numa_faults_locality[2] += pages; |
| |
| p->numa_faults[task_faults_idx(NUMA_MEMBUF, mem_node, priv)] += pages; |
| p->numa_faults[task_faults_idx(NUMA_CPUBUF, cpu_node, priv)] += pages; |
| p->numa_faults_locality[local] += pages; |
| } |
| |
| static void reset_ptenuma_scan(struct task_struct *p) |
| { |
| /* |
| * We only did a read acquisition of the mmap sem, so |
| * p->mm->numa_scan_seq is written to without exclusive access |
| * and the update is not guaranteed to be atomic. That's not |
| * much of an issue though, since this is just used for |
| * statistical sampling. Use READ_ONCE/WRITE_ONCE, which are not |
| * expensive, to avoid any form of compiler optimizations: |
| */ |
| WRITE_ONCE(p->mm->numa_scan_seq, READ_ONCE(p->mm->numa_scan_seq) + 1); |
| p->mm->numa_scan_offset = 0; |
| } |
| |
| static bool vma_is_accessed(struct mm_struct *mm, struct vm_area_struct *vma) |
| { |
| unsigned long pids; |
| /* |
| * Allow unconditional access first two times, so that all the (pages) |
| * of VMAs get prot_none fault introduced irrespective of accesses. |
| * This is also done to avoid any side effect of task scanning |
| * amplifying the unfairness of disjoint set of VMAs' access. |
| */ |
| if ((READ_ONCE(current->mm->numa_scan_seq) - vma->numab_state->start_scan_seq) < 2) |
| return true; |
| |
| pids = vma->numab_state->pids_active[0] | vma->numab_state->pids_active[1]; |
| if (test_bit(hash_32(current->pid, ilog2(BITS_PER_LONG)), &pids)) |
| return true; |
| |
| /* |
| * Complete a scan that has already started regardless of PID access, or |
| * some VMAs may never be scanned in multi-threaded applications: |
| */ |
| if (mm->numa_scan_offset > vma->vm_start) { |
| trace_sched_skip_vma_numa(mm, vma, NUMAB_SKIP_IGNORE_PID); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| #define VMA_PID_RESET_PERIOD (4 * sysctl_numa_balancing_scan_delay) |
| |
| /* |
| * The expensive part of numa migration is done from task_work context. |
| * Triggered from task_tick_numa(). |
| */ |
| static void task_numa_work(struct callback_head *work) |
| { |
| unsigned long migrate, next_scan, now = jiffies; |
| struct task_struct *p = current; |
| struct mm_struct *mm = p->mm; |
| u64 runtime = p->se.sum_exec_runtime; |
| struct vm_area_struct *vma; |
| unsigned long start, end; |
| unsigned long nr_pte_updates = 0; |
| long pages, virtpages; |
| struct vma_iterator vmi; |
| bool vma_pids_skipped; |
| bool vma_pids_forced = false; |
| |
| SCHED_WARN_ON(p != container_of(work, struct task_struct, numa_work)); |
| |
| work->next = work; |
| /* |
| * Who cares about NUMA placement when they're dying. |
| * |
| * NOTE: make sure not to dereference p->mm before this check, |
| * exit_task_work() happens _after_ exit_mm() so we could be called |
| * without p->mm even though we still had it when we enqueued this |
| * work. |
| */ |
| if (p->flags & PF_EXITING) |
| return; |
| |
| if (!mm->numa_next_scan) { |
| mm->numa_next_scan = now + |
| msecs_to_jiffies(sysctl_numa_balancing_scan_delay); |
| } |
| |
| /* |
| * Enforce maximal scan/migration frequency.. |
| */ |
| migrate = mm->numa_next_scan; |
| if (time_before(now, migrate)) |
| return; |
| |
| if (p->numa_scan_period == 0) { |
| p->numa_scan_period_max = task_scan_max(p); |
| p->numa_scan_period = task_scan_start(p); |
| } |
| |
| next_scan = now + msecs_to_jiffies(p->numa_scan_period); |
| if (!try_cmpxchg(&mm->numa_next_scan, &migrate, next_scan)) |
| return; |
| |
| /* |
| * Delay this task enough that another task of this mm will likely win |
| * the next time around. |
| */ |
| p->node_stamp += 2 * TICK_NSEC; |
| |
| pages = sysctl_numa_balancing_scan_size; |
| pages <<= 20 - PAGE_SHIFT; /* MB in pages */ |
|