qemu线程池:一个semaphore的使用范例

qemu里面有个服务于aio的线程池:

qemu在thread_pool_init_one建池子的时候注册了一个延迟执行的work:
pool->new_thread_bh = aio_bh_new(ctx, spawn_thread_bh_fn, pool);

这个work将在spawn_thread里面被安排调度

当work被调度到的时候,执行spawn_thread_bh_fn

这里采用了递归的方式来一次性创建多个worker,新创建的worker线程会递归调动do_spawn_thread来创建下一个worker,直到发现new_threads为0

这里有个问题,如果qemu_thread_create创建新线程失败,那么就会导致后面新的线程永远无法创建,因为pending_threads不会被减扣为0,前面spawn_thread就不会再安排新的下半部工作来创建线程了。

下面是aio派发任务的函数thread_pool_submit_aio,可以看到,池子中的线程数是动态增加的,如果有空闲的线程或者线程数已达上限是不会创建新的线程的,并且采用了信号量通知的方法来减少线程轮询开销,有任务的时候才放开一个额度,而不是让线程一直尝试拿后面的mutex锁再去看下request list是不是空。

worker创建的时候以及worker在func执行完后会再次尝试获取pool->sem(等待sem>0),等待新的任务:

池子资源释放的时候,会标记pool->stopping并给所有worker一个最后的任务(通过sem_post):释放自己:

 

KVM shared MSRs

有些msr寄存器在用户态才有可能访问,内核态不会访问,那么我们vmexit的时候是不需要切换到host值的,而且vmcs里面没有保存相应的寄存器值,只在vcpu需要返回到用户态的时候才把这些msr的值保存到shared_msrs里面,同时切换到host之前保存的值。avi大神的patch解释了这种可以不用在内核态切换的寄存器的优化思路:https://lore.kernel.org/patchwork/cover/170941/

VMCS本身只对很少的一些MSR寄存器进行切换,所以原来大部分MSR的切换是依赖软件进行的,软件切换的开销很大,上述patch则是对此的优化。

这些寄存器一定不会在内核态被访问吗?KVM模块能保证自己不去访问,但是如何保证在内核开抢占的情况下,其他内核代码不会访问这些寄存器呢?AMD的svm里面比较诚实的提出了这种担心,侧面佐证了这确实是个隐患,需要开发者心中有数:
shared_msrs里面host值来自于vcpu_enter_guest调用kvm_x86_ops->prepare_guest_switch(vcpu)的时候保存的值。
vcpu返回用户态切换的过程是调用kvm_on_user_return函数,而这个函数也就是在kvm_x86_ops->prepare_guest_switch(vcpu)的时候调用kvm_set_shared_msr配置的,后者调用了内核函数:user_return_notifier_register(&smsr->urn); 这个函数是内核提供的返回用户态时刻的通知链接口,kvm用它来给vcpu返回挂了个钩子,如此实现了vcpu ioctl系统调用返回qemu时将shared_msrs切换回host之前保存的值。
kvm_on_user_return就是注册到内核的vcpu返回用户态时执行的逻辑,在切换shared_msrs的同时也把自己从通知链上注销了,因为一方面不需要别的进程在退出的时候也执行这个函数另一方面在vcpu enter guest的时候还会再注册的。

MSR从HOST切换到GUEST

该过程发生在vmentry时,最后调用链如下:
vcpu_enter_guest
    kvm_x86_ops->prepare_guest_switch(vcpu) ==> vmx_save_host_state
        kvm_set_shared_msr(vmx->guest_msrs[i].index, vmx->guest_msrs[i].data, vmx->guest_msrs[i].mask)
从host切到guest,guest的msr值从vmx->guest_msrs[i].data这里来
vmx->guest_msrs在vmx_get_msr时被读取,vmx_set_msr时被更改,vmx_get/set_msr一般在handle_rdmsr/wrmsr以及热迁移前后做save load msrs时被qemu触发调用。vmx_set_msr里面同样会调用kvm_set_shared_msr,这里其实更多是为了试一下是否可以设置成功,如果设置不成功那么要让vmx->guest_msrs里面相应的msr值保持原来的设置而不去更新,因为可能新设置的是个非法的值。实际上vmx_set_msr只需要更新guest_msrs就可以了,因为vmentry的时候还是会从guest_msrs里面读取再kvm_set_shared_msr。

MSR从GUEST切换到HOST

vmexit时并不会切share_msrs涉及到的msr,而是发生在vcpu要返回user mode之际,内核调用前面注册的通知函数,调用链如下:
prepare_exit_to_usermode
    exit_to_usermode_loop
        fire_user_return_notifiers
            urn->on_user_return(urn) ==> kvm_on_user_return 找到对应的shared_msrs并恢复到vmentry前的host值

 

尝试讲清楚编辑距离求解

这篇文章是尝试给像作者一样的算法爱好者(业余的委婉说法)解释一下编辑距离求解方法的思考过程,其实也是为了强迫自己真正的吃透编辑距离求解的方法论,以期能达到举一反三的效果。根据以上受众定位,我首先还是引入一段对编辑距离的解释,然后再从递归到递推(动态规划)讲讲对这个问题求解本身的正向思考过程,而不是拿到一个解法来说明它的正确性。

编辑距离的定义是用增删改三种操作将一个字符串演变为一个目标字符串所需的操作次数。比如将horse转化为ros,可以是如下步骤:
1)删除h变成orse; 2)删除o变成rse; 3)增加o变成rose; 4)删除e变成ros
也可以是如下步骤:
1)改h为r变成rorse; 2)删r变成rose; 3)删e变成ros
显然第二种策略要优于第一种,但是这种演变策略的排列组合随着源串a和目的串b的长度增长会几何级数的增长。我们需要用程序来求解。

这是一个典型的考动态规划的编程题,很多文章都会给出一个递推公式,但是这个递推公式为何正确,得到这个公式的思考过程是啥,为什么这么思考?回答了这几个问题才算理解了编辑距离的精髓,而且才能有举一反三的可能性。看了很多解释的文章,感觉都是从结果来理解和解释递推过程反证过程的有效性,隔靴搔痒,没有讲到这个公式是怎么得来的,怎么一下子就能得出这么精妙的计算方法,感觉就像现在解释机器学习模型的原理那样,训练出来一个有效的神经网络结构,然后再试图解释每一层是在干嘛,然而并不是一开始设计的时候就知道会是这样。但编辑距离这个算法是完全由人想出来的,必然是有设计者的心路历程可循。下面讲下我对这个问题的理解思路。

最容易想到的方法是递归,递归函数以目标字符串和源字符串为输入参数,代码如下:

Continue reading “尝试讲清楚编辑距离求解”

vfio 直通设备的 memory region 初始化

vfio特别用一个数据结构来管理设备的memory region
注意到它还有个域是VFIOMmap结构的指针:
感觉是不是有冗余,那么外层结构里面的mem是不是指向内层中的mem呢?
上面的mem还有个奇怪的注释:slow,难道还有一种快一点的MR?那是不是指这里是IO的MR,另外还有个RAM的mr?

Continue reading “vfio 直通设备的 memory region 初始化”

AI来了。云计算凉了?

笔者身处云计算行业,如今的IT界言必称AI给我带来了巨大的精神压力,无时不在思考AI和云计算的关系,云计算在AI大潮下是不是已经明日黄花了,是不是要转行做AI?所谓人类一思考上帝就发笑,我觉得上帝没这么肤浅,毕竟咱也是上帝的AI产品不是~,有空还是要自我迭代一下,继续自觉优化下肉神经网络参数的

Continue reading “AI来了。云计算凉了?”

IOMMU group and ACS cap

VFIO做VM的设备直通过程中,需要把直通设备所在iommu group里面所有的设备都unbind掉,这是为啥呢,iommu group又是啥,木有遇到该问题的小伙伴你们肯定年轻而富有:)你们的设备都是有ACS的呢,这又是啥,咱先来看看官方文档吧:

Continue reading “IOMMU group and ACS cap”

libvirt 向qemu传文件描述符

libvirt创建的qemu进程里面有一些fd的参数,这些文件是libvirt帮qemu打开的一些设备文件句柄等,比如:
qemu … -netdev tap,fd=24,id=hostnet1,vhost=on,vhostfd=25
因为需要libvirt帮忙先配置好后端以及处于安全考虑;但是qemu起来就是另外一个进程,给个fd号就能直接用了吗,显然不是,下面从代码角度分析下

Continue reading “libvirt 向qemu传文件描述符”

sriov vf get iommu group kernel code trace

VF device driver call:
pci_enable_sriov -> sriov_enable -> pci_iov_add_virtfn -> pci_device_add -> device_add ->
pci bus register a iommu notifier will be called when device_add start to notify:

intel_iommu_init -> iommu_bus_init -> “nb->notifier_call = iommu_bus_notifier;”

ops->add_device:

intel_iommu_add_device:

iommu_group_get_for_dev:
this will find or create an iommu group for the VF device

Linux 内核 schedule时的preemption notify机制

内核进行进程切换时,先调用了__schedule,在关抢占后调用context_switch:
prepare_task_switch里面调用fire_sched_out_preempt_notifiers,进而调用prev进程注册的sched_out操作,
这里是分支:如果是新创建的进程被调度了,要调用schedule_tail:
回到主题:finish_task_switch这里会调用fire_sched_in_preempt_notifiers:
preempt_notifiers在哪里注册的呢,对kvm来说是这里:

kvm模块加载的时候vmx_init->kvm_init里面初始化了kvm_preempt_ops

所以kvm_sched_in和kvm_sched_out被调用时的上下文是关抢占的