(WIP)SSD上的I/O电梯算法与HugePage设置可能导致的Crash(by quqi99)

作者:张华  发表于:2016-03-24
版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明
( http://blog.csdn.net/quqi99 )

 

问题

虚机里的某个进程Hang住了,使用”cat /proc/diskstats”命令查看一个SSD硬盘上有很多请求列队。


hung_task_timeout_secs参数与D状态

进程等待IO时如果处于D状态,即TASK_UNINTERRUPTIBLE状态,处于这种状态的进程不处理信号,所以kill不掉,如果进程长期处于D状态,那么肯定不正常,原因可能有二:
  1. IO路径上的硬件出问题了,比如硬盘坏了(只有少数情况会导致长期D,通常会返回错误);
  2. 内核自己出问题了。
这种问题不好定位,而且一旦出现就通常不可恢复,kill不掉,通常只能重启恢复了。内核针对这种开发了一种hung task的检测机制,基本原理是:定时检测系统中处于D状态的进程,如果其处于D状态的时间超过了指定时间(默认120s,可以配置),则打印相关堆栈信息,也可以通过proc参数配置使其直接panic (也可能内核发生了其他错误触发crash,crash时间过长越过2分钟它自己的进程也会被hung task误检测出来为D状态)。
1)设置timeout时间:
   echo 120 > /proc/sys/kernel/hung_task_timeout_secs
2)设置hung task后是否触发panic
   echo 1 > /proc/sys/kernel/hung_task_panic
 

Hugepage与D状态

逻辑地址到线性地址的转换由分段来做,线性地址到物理地址的转换由分页来做。默认每页是4K,为了减少分页映射表的条目,可以增加页的尺寸,Hugepage因此得名。THP(Transparent Huge Pages)是一个使管理Huge Pages自动化的抽象层(由于实现方式问题,THP会造成内存锁影响性能,尤其是在程序不是专门为大内内存页开发的时候,因为khugepaged会在后台扫描所有进程占用的内存,在可能的情况下会把4K page交换为Huge Pages,在这个过程中需要分配内存锁影响性能)。在redhat6中由khugepaged进程在开机时自动启动,如果想关闭它的话可以:
1, echo "never" > /sys/kernel/mm/redhat_transparent_hugepage/enabled
   cat /sys/kernel/mm/redhat_transparent_hugepage/enabled 
   always madvise [never] 
2, or using cmdline, transparent_hugepage=never
never选项将清除TRANSPARENT_HUGEPAGE_FLAG和TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG中的bit值,

 

内存锁与D状态

有进程为了使其不被转换到SWAP中降低效率会调用mlock来锁定内存,mlock会调用lru_add_drain_all对每个cpu下发work函数lru_add_drain_per_cpu回刷pagevec,然后调用flush_work等待完成,flush_work(kworker)又会调wait_for_completion,继尔调用do_wait_for_common将进程设置为D状态。

static long __sched
wait_for_common(struct completion *x, long timeout, int state)
{
return __wait_for_common(x, schedule_timeout, timeout, state);
}

static inline long __sched
__wait_for_common(struct completion *x,
 long (*action)(long), long timeout, int state)
{
might_sleep();

spin_lock_irq(&x->wait.lock);
timeout = do_wait_for_common(x, action, timeout, state);
spin_unlock_irq(&x->wait.lock);
return timeout;
}

do_wait_for_common(struct completion *x,
  long (*action)(long), long timeout, int state)
{
if (!x->done) {
DECLARE_WAITQUEUE(wait, current);


__add_wait_queue_tail_exclusive(&x->wait, &wait);
do {
if (signal_pending_state(state, current)) {
timeout = -ERESTARTSYS;
break;
}
__set_current_state(state);
spin_unlock_irq(&x->wait.lock);
timeout = action(timeout);
spin_lock_irq(&x->wait.lock);
} while (!x->done && timeout);
__remove_wait_queue(&x->wait, &wait);
if (!x->done)
return timeout;
}
x->done--;
return timeout ?: 1;
}
 

缺页时的D状态

其他进程缺页时也会进入D状态(缺页的都会是D状态), 缺页中断服务程序do_page_fault会调用down_read(&mm->mmap_sem)加读锁,然后再调__down_read, 再调rwsem_down_failed_common设置D状态:
    /* wait to be given the lock */
    for (;;) {
        //已经获取读锁,跳出死循环
        if (!waiter.task)
            break;
        schedule();  //否则,调度出去,放弃CPU
        //调度回来后重设状态
        set_task_state(tsk, TASK_UNINTERRUPTIBLE);
    }
    //获取锁后改成R状态
    tsk->state = TASK_RUNNING;

 

如何crash的

这个网页(http://www.oenhan.com/rwsem-realtime-task-hung)描述了一种crash的情况,上述的kworker进程进入了D状态,如果这时有另外一个实时进程H的优先级大于kworker的话,采用FIFO的调度模式,且H在大量业务的话那它会一直抢占CPU不释放,那样导致kworker一直得不到调度,直接导致kworker进程D状态触发hung_task_timeout_secs直接死掉。
 
同一个CPU核上进程有一个H2,而且H1和H2都是被cpu绑定到5核上,H2处理主要业务,H1辅助,H2实时优先级高,H1实时优先级低,当H2压力大占用100% CPU时,H1就得不到调度了。
高业务压力下,H2一直占用CPU,是R状态,H1得不到调度,是S状态。首先是khugepaged扫描时获取写锁,同时包含H2的多个H线程因缺页中断申请读锁,H2处于D状态被schedule让出CPU,H1也恰好缺页中断,获得CPU后申请读锁,排在H2后面,也是D状态。khugepaged释放写锁后,khugepaged先将所有等待读锁的进程拉入读写锁,并将其置成TASK_WAKING状态(参考__rwsem_do_wake函数)。H2先调度回来获取了读锁并完全占用了5核CPU,本来读锁支持并发,但H1此时没有了CPU可供使用,没有调度,一直在schedule打转,虽然进程没有调度,但H1已经被khugepaged拉到读写锁,占用了读锁,一直不释放,khugepaged申请写锁也不能完成,后续更多H缺页中断申请读锁也被阻塞住,D状态达到20s。至于后续能恢复,是因为H2再次因为缺页要申请读锁,排队到队列,进入D状态,schedule让出CPU,H1才能再次得到CPU,完成工作后释放读锁。
 

解决办法

虚机里的I/O路径是: Application -> File/Block in VM -> virtio-blk driver in VM -> virtio-backend driver in Host -> File/Block in Host -> SSD disk in Host 
  • VM里面(File/Block in VM -> virtio-blk driver in VM), 因为跑在QEMU里,Guest机的I/O调度算法应该使用elevator=noop
  • Hypervisor端(virtio-blk driver in VM -> virtio-backend driver in Host), 两种I/O机制(io='native' or io='threads'),五种缓存机制(writethrough, writeback, none, directsync, unsafe), 例:<driver name='qemu' type='raw' cache='writethrough' io='native'/>
  • Host里面(File/Block in Host -> SSD disk in Host ),由于使用SSD, I/O调度算法应该使用elevator=noop
故需要在grub中添加cmdline启动参数及关闭khugepaged进程: transparent_hugepage=never elevator=noop , 另外,
下图的host page cache指bio, disk cache指i/o schedule之后storage driver里的cache:
  • writeback, 数据到达宿主机的page cache就返回成功, vm要保证数据一致性时需要sync。例如vm使用ext4文件系统的话,如果jounal写在page cache时丢失了,这样可能会造成ext4文件系统read only
  • writethrough, 数据写到磁盘后才返回成功(vm的驱动要支持禁止写cache功能),但从宿主机的page cache读、
  • none, 数据写到队列才返回成功
  • directsync,数据写到磁盘才返回成功

举个例子: 如果虚机占用的内存大且多是读少是写的情况,这种情况使用writethrough能利用host cache会快,但是反而会造成host机器上也需要更多的内存去做memory, 当memory不够用的时候,这些qemu进程会相互之会thrashing(可以查看pgsteal, 也可看到大量的swapin, swapout),所以这种情况下使用directsync反而更优(host上不使用cache, 只是在guest里做一个cache占用一些内存). 如果这样设计的话,可以将swap关闭,也可以将IO Schedule改为CFQ(因为此时未使用host cache,因此保证每个qemu进程的公平更为重要)

但使用directsync时即使guest没怎么read也会在host上使用iotop看到大量的disk_read,那有可能是因为qcow2镜像的原因,qcow2需要read原始image(写时才写增量),改为raw格式可能会更好.同时是否做了软raid之间在做resync呢?

 
 
 

附件一,如何确认I/O调度算法

hua@node1:~$ sudo lsblk -io KNAME,TYPE, SCHED
KNAME TYPE SCHED
sda   disk deadline
sda1  part deadline
sda2  part deadline
sda5  part deadline
sda6  part deadline
sda7  part deadline
sda8  part deadline
sda9  part deadline
sda10 part deadline
sdb   disk deadline

hua@node1:~$ cat /sys/block/sda/queue/scheduler 
noop [deadline] cfq

http://www.circlingcycle.com.au/Unix-sources/Linux-check-IO-scheduler-and-discard-support.pl.txt
hua@node1:~$  perl ./Linux-check-IO-scheduler-and-discard-support.pl 
INFO: File systems and raids
NAME    FSTYPE LABEL MOUNTPOINT
sda                  
├─sda1               [SWAP]
├─sda2               
├─sda5               /bak
├─sda6               /data1
├─sda7               /win
├─sda8               /data2
├─sda9               /
└─sda10              /images
sdb                  


INFO: Block devices
NAME    ALIGNMENT  MIN-IO OPT-IO PHY-SEC LOG-SEC ROTA SCHED    RQ-SIZE
sda             0    4096      0    4096     512    1 deadline     128
├─sda1          0    4096      0    4096     512    1 deadline     128
├─sda2       1024    4096      0    4096     512    1 deadline     128
├─sda5          0    4096      0    4096     512    1 deadline     128
├─sda6          0    4096      0    4096     512    1 deadline     128
├─sda7          0    4096      0    4096     512    1 deadline     128
├─sda8          0    4096      0    4096     512    1 deadline     128
├─sda9          0    4096      0    4096     512    1 deadline     128
└─sda10         0    4096      0    4096     512    1 deadline     128
sdb             0 1048576   2048     512     512    1 deadline     128


INFO: I/O elevator (scheduler) and discard support summary
INFO: Hard Disk sda configured with I/O scheduler "deadline" 
INFO: Hard Disk sdb configured with I/O scheduler "deadline" supports discard operation
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

quqi99

你的鼓励就是我创造的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值