减少 per-cpu kthreads引发的系统抖动

来源:互联网 发布:ubuntu 查看机器配置 编辑:程序博客网 时间:2024/06/07 18:01

出自:linux/Documentation/kernel-per-CPU-kthreads.txt

背景

  在一些高吞吐量、实时要求极高的系统中任务一旦运行起来就期望它能够持续的占用cpu,在这样的系统中任务被打断会造成性能或者计算实时性的“抖动”。而在Linux中,内核活动打断、甚至挤占用户态任务的时间都是有可能发生的:包括外部中断、异常、内核线程等等。而此就linux内核中的per-cpu kthreads(每cpu内核线程)列出了减少per-cpu kthreads对系统性能抖动影响的一些方法。Non-per-CPU 内核线程不在本文讨论范围内,因为可以将它们绑定到一个其他CPU上以避免对我们自己的自己CPU上计算任务的影响。

  下面就针对不同的内核任务,介绍了一些减少其产生的性能抖动的方法。

一、KTHREADS内核线程

1.1  ehca_comp/%u线程

  Ehca_comp是eServer eHCA Infiniband设备驱动创建的线程,在此设备驱动初始化过程中在每个online CPU上创建的线程ehca_comp/%u,周期性处理设备驱动相关工作。
  处理方案(do any of the following):
(1)不使用该设备或者内核配置选项中不使能该设备驱动,从源头上消除了这个设备驱动产生的per-cpu内核线程;
(2) 将eHCA Infiniband 设备相关的任务放以及中断绑定到其他CPUs上执行;
(3) Rework eHCA驱动,使它的per-cpu内核线程只在指定的cpu上运行。

1.2  irq/%d-%s

  在内核使能了CONFIG_IRQ_FORCED_THREADING并在内核启动参数中传入了threadirqs参数后,系统会将中断(标记为IRQF_NO_THREAD的除外)线程化处理。
  处理方案:
  (1) 将中断亲和到其他cpu上,这样中断化线程就被只能限制于指定的cpu运行。

1.3  kcmtpd_ctr_%d

  作用:蓝牙驱动相关的内核线程。
  处理方案  (do one of the following):
  (1) 禁用蓝牙设备或者驱动,其相应的内核线程也就不会创建运行;
  (2) 将蓝牙设备的中断绑定到其他CPUs上, 将蓝牙相关的应用都迁移到其他CPUs上执行。

1.4  ksoftirqd/%u软中断线程

  软中断内核线程。在linux中,硬中断处理程序完成后会调用软中断处理耗时任务,但此时处理软中断最多MAX_SOFTIRQ_RESTART次,若超过这个数量则留给ksoftirqd软中断线程来处理;另外内核中主动调用raise_softirq时如果正处于中断上下文,也会唤醒ksoftirq线程执行软中断。Ksoftirqd调度策略为SCHED_FIFO,优先级为1(99为最高),每个都会创建一个ksoftirqd线程。在被唤醒后,它会遍历所有的软中断中pending的任务。

  处理方案:软中断向量表中的不同的软中断按下面的方案进行单独处理。

1.4.1 TIMER_SOFTIRQ (Do all of the following)

  (1) 首先,让我们任务运行的CPU尽量避免陷入到内核态,如减少此CPU上的系统调用;然后 将内核线程、中断绑定到其他CPU;
  (2) 首先,内核配置上CONFIG_HOTPLUG_CPU=y后启动内核;其次, 通过/sys/devices/system/cpu/cpuN/online接口将指定CPU强制offline,再online它们,这样就可将定时器软中断处理线程迁移到其他CPU上。 

  如果要清理多个CPU的TIMER_SOFTIRQ,方法也一样:先offline我们需要清理的CPUs,然后再online它们。

  需要注意的是,一旦已经online了指定的这些CPUs后,不要再offline其他的CPUs,因为这又会将timer软中断线程重新迁回到我们指定的这些CPUs上。

1.4.2 NET_TX_SOFTIRQ/NET_RX_SOFTIRQ (Do all of the following)

  网络收/发软中断,分别处理数据包的发送和接收。

  (1) 将网卡中断绑定到其他CPUs上,这样软中断也会默认硬中断的cpu上被执行;

  (2) 将网络IO初始化放到其他cpu上进行;
  (3) 应用启动后,要避免cpu热插拔以免其他任务跑到我们的cpu上引发性能抖动。

1.4.3 BLOCK_SOFTIRQ/BLOCK_IOPOLL_SOFTIRQ (Do all of the following)

  块设备软中断和块设备IO轮询软中断。
  (1) 将块设备中断绑定到其他CPUs,这样对应的软中断也会默认在上面执行;
  (2)  将块设备IO初始化和块设备轮询放到其他CPU上执行;
  (3) 我们任务运行的CPU要尽量避免热插拔,以免任务迁移影响这些CPU性能。

1.4.4 TASKLET_SOFTIRQ  (Do one or more of the following)

   Tasklet软中断线程。
  (1) 减少含有tasklets机制的设备驱动;(使用了tasklets的驱动可能会调用tasklet_schedule()这样的函数);
  (2) 如果无法避免使用含有tasklets的设备驱动,可以尝试使用工作队列来替代驱动中的tasklets所做的工作;

  (3) 将提交tasklet的任务放到其他cpu上执行,这样产生的tasklet软中断线程也会"继承"在其他的CPU上执行。

1.4.5 SCHED_SOFTIRQ (Do all of the following)

  调度软中断用于触发run_rebalance_domains()函数在进行各个CPU的load-balance。
  (1) 尽量保证一个CPU只运行我们的一个个关键任务,还要避免IPI触发核间调度;例如,一个任务被唤醒时会为它准备一个可运行目标CPU,如果这个目标CPU与唤醒此任务(执行try_to_wake_up()的CPU)的CPU不共享cache,则会将被唤醒任务加入到目标CPU的wake_list中,并向目标CPU发送一个scheduler IPI中断 (见注释1),目标CPU在IPI中断处理函数中则会raise SCHED_SOFTIRQ软中断。
  (2) 使能CONFIG_RCU_NOCB_CPU=y, CONFIG_RCU_NOCB_CPU_ALL=y, CONFIG_NO_HZ_FULL=y内核选项,并在内核启动参数中添加"nohz_full=cpuN" 以将我们运行关键任务的CPU设置为adaptive-ticks(动态滴答,见注释2) CPU。这样可以减少指定CPU的调度时钟中断,使得在CPU上发生负载均衡的概率降到最低,即发生SCHED_SOFTIRQ的概率降到最低。
  (3) 尽量保持我们关键任务的CPU(non-idle)运行在用户态。例如,尽量避免在此CPU上进行系统调用,同时将内核线程和中断亲和到其他CPUs。这可以大大减少调度时钟中断的发生。

1.4.6 HRTIMER_SOFTIRQ (Do all of the following):

  (1) 尽量保证我们的CPU(non-idle)运行在用户态。减少此CPU上的系统调用,同时将内核线程和中断尽可能亲和到其他CPUs。
  (2) 使能CONFIG_HOTPLUG_CPU=y内核选项,内核启动后强制offline我们计算任务的CPU,然后再online,以便将定时器迁移到其他CPUs。需要注意,后续不能再offline其他的CPUs,因为那样其他CPUs上原先的定时器又会重新迁移过来。

1.4.7 RCU_SOFTIRQ (Do at least one of the following):

  (1) 尽量减少RCU的使用(Offload callbacks),同时让CPU处于dyntick-idle或者adaptive-ticks状态。可以参照如下方式来进行:
    a. 使能CONFIG_RCU_NOCB_CPU=y, CONFIG_RCU_NOCB_CPU_ALL=y,CONFIG_NO_HZ_FULL=y 内核选项,同时内核启动时添加"nohz_full="启动参数将我们的计算任务CPU设置为adaptive-ticks的CPU;将rcuo内核线程绑定到其他CPU上;
    b. 尽量保证我们的计算任务CPU(non-idle)运行在用户态,如减少此CPU上的系统调用,同时将内核线程和中断亲和到其他CPUs上。
  (2) 按照如下方法,可通过dyntick-idle使得RCU在其他CPUs完成任务:
    a. 使能 CONFIG_NO_HZ=y 和 CONFIG_RCU_FAST_NO_HZ=y内核配置;
    b. 尽可能让其他CPUs检测到本CPU已经通过了RCU quiescent state (已经离开了 rcu_lock<-->rcu_un_lock 或者还未进入 rcu_lock<-->rcu_un_lock状态);另外,如果内核使能了CONFIG_NO_HZ_FULL=y,此CPU上有runing状态的任务也可以让其他CPUs检测到此CPU已经通过了quiescent state)。
(参考:http://www.wowotech.net/kernel_synchronization/223.html和https://www.ibm.com/developerworks/cn/linux/l-rcu/)

    c. 尽量保证我们的计算任务CPU(non-idle)运行在用户态,如减少此CPU上的系统调用,同时将内核线程和中断亲和到其他CPUs上。

1.5  kworker/%u:%d%s (cpu, id, priority)工作队列

  处理方案(do any of the following):
  (1) 将任务调度策略设置为实时任务,这杨就可以抢占工作队列线程(kworker daemons);
  (2) 使用WQ_SYSFS参数在alloc_workqueue()创建工作队列,这样创建的工作队列任务在sysfs文件系统可见。接着我们就可利用过/sys/devices/virtual/workqueue/*/cpumask文件来将工作队列任务绑定到其他CPUs上。我们可以通过"ls sys/devices/virtual/workqueue"命令来查看有哪些工作队列任务设置了WQ_SYSFS。

  (3) 视情况选择降下面的方案来降低性能“抖动”
    a 使用CONFIG_SLUB=Y代替CONFIG_SLAB,这可避免slab分配器周期性的在各个CPU上进行cache_reap();
    b 避免运行oprofile,以免wq_sync_buffer()引发的抖动;
    c 限制CPU频率以避免系统为了散热而运行cpu-frequency调节器来降频;如果CPU架构允许(且要正确使用),可以使用CONFIG_CPU_FREQ=n 内核选项以减少CPU-frequency调节程序周期性的运行频率,包括cs_dbs_time()和od_dbs_time();
    d 对于v3.18版本内核,christoph lameter提交的按需创建vmstat workers工作队列的方法可防止SMP系统中vmstat_update()引发的抖动。对于v3.18之前的版本,虽然无法完全消除性能抖动,但是我们也可以往/proc/sys/vm/stat_interval写一个大的数值来减少vmstat_update()运行频率;stat_interval默认值是HZ,即时间间隔为1秒。当然这种方法会有副作用,因为往stat_interval写的值越大virtual-memory 数据更新的越慢。也可以将我们的计算任务改造为实时任务,这样就可以抢占vmstat_update任务,但注意不要将实时任务亲和到CPU上。However, Christoph Lameter在Gilad Ben-Yossef的基础上提交了一个减少甚至消除 vmsta 性能影响的RFC patch,参考:https://lkml.org/lkml/2013/9/4/379.

    e 使用"elevator=noop"内核启动参数,以避免block驱动创建相应的workqueue;
    f  如果是使用high-end powerpc 服务器的情况,配置上PPC_PARTS_DAEMON=n选项可以防止PTAS daemon在perc cpu上周期性的运行,(这需要修改Kconfig文件,and will defeat  this platform's RAS functionality.) 避免rtas_event_scan()引发的抖动;
    g  如果running on Cell Processor,内核配置上CBE_CPUFREQ_SPU_GOVERNOR=n选项可以避免spu_gov_work()引发的os抖动。
    WARNING:检查自己的CPU,确保这样做的安全性。
    h  如果是PowerMAC机器,内核配置CONFIG_PMAC_PACKMETER=n选项,这可以关掉CPU-meter,以免rackmeter_do_timer()引发的OS抖动。

1.6 rcuc/%u 

  加速RCU回调处理的percpu 内核线程,依赖于CONFIG_RCU_BOOST=y内核配置。当CPU堆积了太多RCU回调处理时加速处理避免RCU可能引发的内存耗尽。
  处理方案(do at least one of the following):
  (1) 使能CONFIG_PREEMPT=n内核选项,这样内核就不会创建相关的kthreads,同时也会将RCU priority boosting功能关掉, 这种方法适用于对响应要求不是非常高的任务;

  (2) 关掉CONFIG_RCU_BOOST内核选项,这种方法虽然简单但也有限制,即会关掉RCU priority boosting功能;
  (3) 配置CONFIG_RCU_NOCB_CPU=y以及CONFIG_RCU_NOCB_CPU_ALL=y,这样可以将rcuc回调函数的工作由rcub内核线程来接管处理;

  (4) 避免进入内核态,尤其注意不要执行cpu hotplug操作。

1.7  rcuob/%d, rcuop/%d, and rcuos/%d

  用于接管rcuc回调函数的内核线程

  处理方案( do at least one of the following):

  (1) 这些内核线程亲和到其他cpu上运行;

  (2) 去掉CONFIG_RCU_NOCB_CPU内核配置,虽然可以阻止这些内核线程创建,但是又会导致rcuc内核线程可能引发的抖动。

1.8  watchdog%u

  Watchdog内核线程用于检测各个cpu上的software lockups。

  在配置了CONFIG_WATCHDOG的系统中,每个cpu会对应一个watchdog内核线程,且其优先级为SCHED_FIFO-99(系统最高优先级)。watchdog每1000ms被唤醒一次,然后去更新软件狗时间戳。时钟中断处理程序会检查当前时间与上次时间戳的间隔是否大于用户设置的值。如果大于用户设置的值则表明watchdog线程(系统最高优先级)很久没有调度了,造成这种的原因可能是1) 系统有高优先级的cpu消耗性任务(FIFO-99); 2) 关中断时间太长。 此时watchdog线程会大于内核当前的调用执行路径的调用堆栈。如果配置了CONFIG_DETECT_SOFTLOCKUP,如果watchdog得不到调度会产生内核告警。

  解决方案 (do at least one of the following):

  (1) 内核去掉CONFIG_WATCHDOG配置项;

  (2) 通过 echo 0 > /proc/sys/kernel/watchdog关掉watchdog;(

  (3) 通过echo为/proc/sys/kernel/watchdog_thresh写一个很大的值减少watchdog运行机会。

参考

中断绑定与亲和性:参考Documentation/IRQ-affinity.txt;
利用cgroup将任务绑定到指定CPU集合:参考Documentation/cgroups;
利用taskset命令来将任务绑定到指定CPU集合:参考 man taskset:  
参考man sched_setaffinity: 使用sched_setaffinity()系统的调用来绑定CPU
定位cpuN上发生的系统抖动:
cd /sys/kernel/debug/tracingecho 1 > max_graph_depth # 写入值越大,越详细echo function_graph > current_tracer# run workloadcat per_cpu/cpuN/trace

注释1IPI调度中断的触发的情况如下:

  try_to_wake_up-->ttwu_queue-->ttwu_queue_remote()-->smp_send_reschedule。

  对于x86实际上调用native_smp_send_reschedule-->apic->send_IPI_mask()就触发了IPI中断。

注释2

  CONFIG_NO_HZ:可用以减少特定cpu上内核中断(时钟,ipi等),一旦检测到cpu为idle则不产生时钟/ipi中断;

  CONFIG_NO_HZ_FULL:可减少特定cpu上内核中断(时钟,ipi等),一旦检测到有处于R状态的进程就不产生时钟中断/IPI中断;


原创粉丝点击