Liuw's Thinkpad

想要赢就先学会输,想要成功就先学会失败

Archive for the ‘Tech’ Category

Xen中的Continuation

with 2 comments

我个人理解的计算机科学中的Continuation,即是把一个计算过程具体化(reifier),得到一组可以用来重构这个计算过程的结构。通常情况下,它包含了处理器的栈,还包含了当前的执行点(具体化一点,就是所有寄存器的值),这样处理器就可以在任何时候重构这个计算过程。

一些高级语言,如Python、Ruby、Haskell、LISP等,在VM中对Continuation提供了支持。目前使用的计算机,还是必须手工编写代码才能支持Continuation。

Xen提供了一个hypercall_create_continuation函数,用来resume当前的hypercall。具体做的事情无非就是为客户虚拟机准备好上下文(栈及寄存器),然后重新执行陷入指令。要让虚拟重新执行陷入指令的处理又分为再种情况:假如是PV的虚拟机,把EIP减2,重新执行int 0×82或者syscall指令;假如是HVM,那么不需要对EIP作处理,因为vmcall产生的是fault,指令会重新自动执行。

Xen中的Continuation仅仅用于重新执行hypercall,对栈和寄存器的操作比较少,速度是有保证的。

那么,Xen在什么时候需要创建Continuation呢?常见的代码形式是:

if ( hypercall_preempt_check() )
    hypercall_create_continuation(...)

hypercall_preempt_check又是干什么的呢?假如当前物理CPU有等待处理的softirq,或者PV中的VCPU中有可投递的upcall(pending且!masked),那么它的返回值就为真。(HVM这里先不讨论)

即是说,Xen在执行某些Hypercall的时候,有可能处于一种不能抢占CPU的状态,所以必须让客户虚拟机重新再执行一次Hypercall。

受影响的Hypercall有:

__HYPERVISOR_hvm_op
__HYPERVISOR_mmuext_op
__HYPERVISOR_mmu_update
__HYPERVISOR_domctl
__HYPERVISOR_set_trap_table
__HYPERVISOR_memory_op
__HYPERVISOR_multicall
__HYPERVISOR_console_io

本人目前的看法是,这些Hypercall在有upcall或者softirq处于pending的状态时候执行的话,都会有影响虚拟机状态一致性的可能。以console io为例,虚拟机和consoled之间的事件通知,就是用的event channel机制。处于pending状态的event很可能就是IO通信产生的。假如虚拟机在调用__HYPERVISOR_console_io之前没有先把所有的event都处理完,那么就会造成数据的丢失。

但是其他的Hypercall为什么不会有这样的问题呢?那自然是因为其他的Hypercall在执行的时候可以保证当时所处的状态是“干净”的。

草草看了一下代码,写下了上面的文字。水平所限,错误在所难免,欢迎指正。

Written by liuw

February 28th, 2011 at 2:33 pm

Posted in Tech

Tagged with , ,

流量控制的一个小问题记录

without comments

洗澡时突然回想起自强邮件列表上一个学弟提到的问题,初步问题是如何让不同的程序走不同的网络(Web浏览器和BT),深层次的问题是如何让不同的协议走不同的网络。

我初步提出的解决办法有两个:一是设置本地代理;二是使用iptables的l7-filter。

代理的实现原理是让代理选择出口,这样只要为两个程序开两个本地代理即可。这解决的是“初步问题”。

用l7-filter是为了解决“深层次问题”。我个人没有使用过l7-filter,我的想法是,只要能把流量标记出来,那么就可以处理。然后学弟说他了解到l7-filter主要是用来做流量控制的,没有这个功能。当时也有其他事情,没有多想。

现在回想起来,做还是可以做的,虽然我没有自己做实验,但是我已经想像到整个流程是怎么样的了。也怪我原来说得不够清楚,只提到了l7-filter。其实l7-filter只是完成标记流量的功能,真正的流量整形,还是得靠tc。

好吧,那么现在我们可以把深层次的要求看作一个设置QoS的问题,那么问题就很好办了。

流程有两步:1. iptables及l7-filter为流量设置标记(–set-mark);2. tc设置QoS参数,把不允许被mark数据流流出的接口对应数据流的速率设置为0(这话挺拗口的)。

具体我就不做了。找点参考文档放下面吧。

  • http://blog.edseek.com/~jasonb/articles/traffic_shaping/scenarios.html,这里面有用tc设置速率的例子。
  • http://lartc.org/,Linux高级路由及流量控制。

Written by liuw

January 30th, 2011 at 12:12 am

特权高就是好

without comments

Virtual Machine Introspection就是说只看不改。要是改一下数据会怎么样?

今天就干了这事,在Xen的层面把程序的代码内容给改掉了。简单点说,这就是一种代码注入。

有趣的地方是,改了的机器码最后反映到程序映像之中了。想了一下,大概是因为代码段是以文件映射的方式映射到内存之中的,程序退出的时候会让磁盘上的文件和内存中的内容同步。

往好的方面想,这样的功能是目前最强的hot patch方式。一方面可以on the fly地改进程,另一方面顺带把映像也改了。

往不好的方面想,这样的功能就太可怕了,自己的程序被改了也不知道,重新启动也不成。

不过我原来没有料到会有映像的同步。我一向的理解是这样的只读映射最后只需要把内容丢弃就可以了,代码以后有机会再看吧。

Written by liuw

December 30th, 2010 at 4:11 pm

Posted in Tech

Tagged with ,

内核级开发的一些感悟

without comments

出了问题,第一步就应该是检查typo,而不是逻辑和设计。

昨天写的代码dead lock了,一上来就先检查逻辑,这是不对的。后来发现是一个lock写成了unlock。

内核代码的要求是near perfect,要对自己有信心,因为当代码分块足够小时,逻辑上出错误是很难的。

调试很难,printk是最好的朋友。但是归根到底还是要对设计和实现有深刻的理解。个人习惯是在预想的执行路径中放置printk,然后对照实际执行的情况,屡试不爽。

特别注意一些可重入/不可重入的地方,以及一些lock的条件。

Written by liuw

December 15th, 2010 at 11:41 am

Posted in Tech

Tagged with , ,

Xen更新页表的几个入口

without comments

这屁事搞了好几天了,这里记一下。

三个入口:

  1. do_mmu_update,个是最正规的ParaVirt入口,可以更新任何一级页表。
  2. do_update_va_mapping,直接更新virtual address对应的L1页表,只能用于L1页表。
  3. writable page table,实际上这是一个Xen的特性,不是一个具体的函数,也只能用于L1页表。Xen先把L1页表unhook让guest可写,然后ptwr_emulated_update为guest模拟写操作,Xen验证写入数据的合法性后再把页表重新hook上。

这是这几天看代码的一些小总结,不能保证完全正确。以后再写代码验证。

XenoLinux内核上面的方面都有采用。比如在Dom0进程要更新页表的时候(创建也好,销毁也好),通常是用do_mmu_update,效率比较高;但是在munmap单个页面的时候,通常是使用writable page table模式。

Written by liuw

December 10th, 2010 at 4:55 pm

Posted in Tech

Tagged with , , ,

一个优良、健壮的随机数生成器是多么有必要啊!

without comments

做演化计算习题有感。Perl的rand实在不怎么好使。

引用John von Neumann一句话来结束:

Anyone who attempts to generate random numbers by deterministic means is, of course, living in a state of sin.

Written by liuw

December 1st, 2010 at 5:54 pm

Posted in Tech

Tagged with ,

关于虚拟化的一些小点

with one comment

VirtIO实质上是PV Driver的一个抽象层,所以它的性能相对纯粹的emulation好很多。这也解释了为什么在VirtIO的网卡上执行ethtool得不到什么信息(如速率),不知道是还没有空加上,还是PV Driver本身的限制。

要想性能再好,那就只有依靠PCI Passthrough了。PCI Passthrough原来有过软件上的实现,但是性能和安全性都不是特别好。现在硬件已经支持了,一些Hypervisor也已经跟进。但是用PCI Passthrough会让动态迁移变得很困难(会不会变得“不可能”?)。

现在PCI-E标准已经支持虚拟化,下一步就是在端设备上实现虚拟化。如PCI-E标准中的Single Root I/O Virtualization,向上提供物理函数及虚拟函数。这时的Passthrough是VM中的驱动与VMM中的接口对接,从而使得带Passthrough的VM可以迁移(个人理解)。

source:
Rusty Russell,VirtIO: towards a de-facto standard for virtual I/O devices
Tim Jones,Linux virtualization and PCI passthrough
Tim Jones,Virtio: An I/O virtualization framework for Linux

Written by liuw

November 24th, 2010 at 4:40 pm

关于虚拟磁盘的一些笔记

without comments

虽然虚拟机可以使用物理硬盘、物理分区来存储系统、数据,但是更多情况下我们还是使用虚拟磁盘来完成这个工作。最简单的虚拟磁盘格式就是raw file,可以使用dd(1)或者qemu-img来创建。如:

$ dd if=/dev/zero of=/PATH/TO/IMG bs=1G count=10

然后把生成的文件作为一个虚拟磁盘挂载到虚拟机中,就可以按照正常的程序去分区,写数据。

在虚拟机关机之后,如果想读取虚拟磁盘分区里面的数据,还得费点操作。首先使用losetup(8)把这个镜像做成loop设备。

# losetup /dev/loop0  disk.img

现在loop0就和sda一样了,可以使用fdisk查看分区情况。挂载里面的分区,应该先知道分区起始地址在设备中的偏移。

# fdisk /dev/loop0 -l -u

这样可以得到一个sector的大小(PC上典型是512 bytes),然后可以知道某个分区的起始sector,那么就可以算出来需要的偏移。

# mount -o offset=`expr $SECTOR_SIZE \* $START_SECTOR` /dev/loop0 /mnt

这样就把分区挂载到mnt分区下了。处理完之后再把loop0 detach即可。

QEMU还支持CoW的虚拟磁盘。这样的磁盘比raw file复杂。

$ qemu-img -f qcow2 disk.img 100G

要想再离线挂载这样的磁盘,需要有nbd的支持。

# modprobe nbd

然后使用qemu-nbd把虚拟磁盘attach到/dev/ndbN上。

# qemu-ndb disk.img -c /dev/ndb0

这样得到的ndb0和上面的loop0是一样的角色,再按照上面的步骤操作即可。最后还是要注意该detach的都detach掉。

Written by liuw

November 23rd, 2010 at 10:25 pm

Posted in Tech

Tagged with , , , , ,

一个简单的KVM网络性能测试(续)

with one comment

一个简单的KVM网络性能测试里面,我用wget和nc对使用VirtIO的KVM虚拟机进行了一些网络性能的测试。但是从观察到的情况来看,得到的数据不能完全说明问题,毕竟不是用通用的测试软件来做的。所以这次使用netperf-2.4.5再测试一次。

还是和上次一样的软硬件配置,进行了三个配置的测试:1. 两个物理机通信;2. 同一物理机上的两个VM通信;3. 不同物理机上的两个VM通信。

每个测试配置中,一台开启netserver,另外一台使用netperf进行连接。样例脚本如下:

#!/bin/sh
TESTS=("TCP_STREAM" "UDP_STREAM" "TCP_RR" "UDP_RR")

for ((i=0;i<${#TESTS[@]};i++))
do
    netperf -t "${TESTS[i]}" -H "$SERVER" -l 120 > netperf_"$TYPE"_"${TESTS[i]}"
done

在对同一物理机上两个VM进行测试时,第一次测试时两个VM的网卡都hang掉了,物理机的网桥还是好的。重启两个VM之后恢复正常。不知道这算不算是一个bug。

对每个配置进行了4个测试。

  1. 两个物理机通信,TCP_STREAM带宽941.40M,UDP_STREAM带宽900.29M;TCP_RR每秒完成9562.31个传输,UDP_RR每秒完成10183.35个传输。
  2. 同一物理机上的两个VM,TCP_STREAM带宽1801.36M,UDP_STREAM带宽1034.40M;TCP_RR每秒完成2736.87个传输,UDP_RR每秒完成2906.29个传输。
  3. 不同物理机上的两个VM,TCP_STREAM带宽550.59M,UDP_STREAM带宽909.48M;TCP_RR每秒完成2049.98个传输,UDP_RR每秒完成2117.30个传输。

Request/Response测试中,payload的大小都是1B;STREAM测试中,TCP payload的大小是16KB,UDP payload的大小是64KB。所以RR测试主要是考查机器处理中断的能力;STREAM主要是考查传输带宽。

配置2中的UDP_STREAM第一次测试只有64M带宽,令人吃惊的小。再次进行测试后变为1034.40M。

和原来的简单测试类似,在TCP_STREAM测试中,配置2的带宽超出了物理网卡的极限——数据只在网桥内进行交换,而且中断次数不是太多。不同物理机上的TCP_STREAM带宽只有物理网卡的一半多点。

在RR测试中,虚拟机完全落败于物理机。虚拟机的调度还有极长的中断处理路径是最大的软肋。

KVM-netperf-test

Written by liuw

November 12th, 2010 at 11:53 am

Posted in Tech

Tagged with , , , , ,

一个简单的KVM网络性能测试

with one comment

硬件:曙光天潮TC2600,CB-65F刀片,Broadcom NetXtreme BCM5715S千兆网卡。

软件:RHEL 5.5,KVM 83。

所有虚拟网卡都以VirtIO模型启动,目前看到的报告是VirtIO的性能比较好。

测试很简单。

host1% nc -l 8000 < /dev/zero
host2% wget target:8000 -O /dev/null
  • 两个物理机,稳定112M/s,接近千兆网卡的极限。
  • 同一物理机上的两个VM,稳定200M/s。
  • 不同物理机上的两个VM,稳定40M/s,峰值80M/s。
host1% nc -l 8000 < /dev/urandom
host2% wget target:8000 -O /dev/null
  • 两个物理机,稳定6.8M/s。
  • 同一物理机上的两个VM,稳定3.44M/s。
  • 不同物理机上的两个VM,稳定3.40M/s。

很有趣的结果。其实我用iptraf看了一下,在zero的情况下,真正的outgoing rate是很低的。虽然wget报的数据很吓人,但是实际上的数据率没有超过10M/s。而且ifconfig看到的RX也与wget累积计数不合。发现VirtIO的一个可能的bug,网卡的TX/RX到4G多一点的时候就会清0。

这样的测试没什么典型性,下一步考虑用Netperf来测试,请期待。

Written by liuw

October 27th, 2010 at 2:57 pm

Posted in Tech

Tagged with , , , ,