nohz load balance选择cpu
如果开启了nohz,在busy的cpu上,每次时钟中断的时候会发起nohz load balance。它的一个关键点是选择一个idle cpu。
static void nohz_balancer_kick(struct rq *rq) { ... out: if (flags) kick_ilb(flags); } static void kick_ilb(unsigned int flags) { int ilb_cpu; ... ilb_cpu = find_new_ilb(); ... smp_call_function_single_async(ilb_cpu, &cpu_rq(ilb_cpu)->nohz_csd); }
可见find_new_ilb负责找和是的idle cpu。
static inline int find_new_ilb(void) { int ilb; for_each_cpu_and(ilb, nohz.idle_cpus_mask, housekeeping_cpumask(HK_FLAG_MISC)) { if (idle_cpu(ilb)) return ilb; } return nr_cpu_ids; }
idle cpu是从nohz.idle_cpus_mask和HK_FLAG_MISC 类型的housekeeping cpu的交集中得到的。nohz是一个全局变量。
static struct { cpumask_var_t idle_cpus_mask; atomic_t nr_cpus; int has_blocked; /* Idle CPUS has blocked load */ int needs_update; /* Newly idle CPUs need their next_balance collated */ unsigned long next_balance; /* in jiffy units */ unsigned long next_blocked; /* Next update of blocked load in jiffies */ } nohz ____cacheline_aligned;
idle_cpus_mask表示可以用于nohz idle balance的cpumask,nr_cpus表示idle_cpus_mask所包含的cpu的数量。has_blocked表示???
哪些cpu会加入idle_cpus_mask?
void nohz_balance_enter_idle(int cpu) { ... cpumask_set_cpu(cpu, nohz.idle_cpus_mask); atomic_inc(&nohz.nr_cpus); ... WRITE_ONCE(nohz.has_blocked, 1); }
该函数会在cpu进入idle状态前调用。
那么housekeeping cpu又是做什么的?
housekeeping cpu是为了减少系统中断,内核线程的影响,将这些内核任务隔离到housekeeping cpu上去。默认情况下所有cpu都被认为是housekeeping cpu。
const struct cpumask *housekeeping_cpumask(enum hk_type type) { if (static_branch_unlikely(&housekeeping_overridden)) if (housekeeping.flags & BIT(type)) return housekeeping.cpumasks[type]; return cpu_possible_mask; }
一般housekeeping_overridden是没有被设置的,所以所有的cpu都被认为是housekeeping cpu。
了解到nohz balance选cpu的范围,还有一个顺序的问题。for_each_cpu_and会从前往后找cpu,也就是排在前面的idle cpu会被选中的概率更大。
可以使用bpftrace来验证一下nohz选cpu的频率。
#!/usr/bin/env bpftrace kfunc:smp_call_function_single_async { @cpu[args->cpu, cpu]= count(); }
浙公网安备 33010602011771号