设为首页 收藏本站
查看: 2030|回复: 0

[经验分享] Qemu-kvm模拟APIC Timer中断

[复制链接]

尚未签到

发表于 2015-10-10 12:38:38 | 显示全部楼层 |阅读模式
Qemu-kvm模拟APIC Timer中断

qemu-kvm模拟两个时钟中断设备PIT(i8254)和APIC Timer设备,也就是产生中断源。两者电子线路连接不同,对于i8254设备来说首先连接到i8259中断控制器,i8259中断控制器再连接到ioapic设备中,送到lapic,最后注入到vcpu中。对于APIC Timer设备实际就是lapic的一个功能,意思就是通过编程可以触发lapic设备周期产生中断,然后注入到vcpu。通过上面了解知道两者区别了APIC Timer的是每个cpu内核都有一个定时器,而PIT是共用的一个。
APIC Timer的模式APIC定时器包含3种定时器模式,周期触发periodic和一次性触发one-shot和TSC-Deadline模式(最新的CPU里面支持)。
下面之分析Qemu-kvm模拟APIC Timer周期触发periodic模式中断过程。

1.创建和初始化模拟定时器设备
由于APIC Timer设备实际就是lapic的一个功能,所以在创建lapic设备同时,也就辅助设置了。
int kvm_create_lapic(struct kvm_vcpu *vcpu)
{
hrtimer_init(&apic->lapic_timer.timer, CLOCK_MONOTONIC,
                     HRTIMER_MODE_ABS);
        apic->lapic_timer.timer.function = kvm_timer_fn;
        apic->lapic_timer.t_ops = &lapic_timer_ops;
        apic->lapic_timer.kvm = vcpu->kvm;
        apic->lapic_timer.vcpu = vcpu;
}
该如何模拟APIC Timer设备呢?对于周期性时钟中断,我们可以采用定时器方式,一定间隔调用一次中断源产生函数,模拟时钟中断发生。
首先创建一个定时器,然后初始化APIC Timer设备包括对设备的操作,设备属于哪个vcpu和vm,设备额产生中断的函数,实际就是定时器回调函数。

2.启动上面定时器,产生调用回调函数

static void start_apic_timer(struct kvm_lapic *apic)
{
        ktime_t now = apic->lapic_timer.timer.base->get_time();

        apic->lapic_timer.period = (u64)apic_get_reg(apic, APIC_TMICT) *
                    APIC_BUS_CYCLE_NS * apic->divide_count;
        atomic_set(&apic->lapic_timer.pending, 0);

        if (!apic->lapic_timer.period)
                return;
        /*
         * Do not allow the guest to program periodic timers with small
         * interval, since the hrtimers are not throttled by the host
         * scheduler.
         */
        if (apic_lvtt_period(apic)) {
                if (apic->lapic_timer.period < NSEC_PER_MSEC/2)
                        apic->lapic_timer.period = NSEC_PER_MSEC/2;
        }

        hrtimer_start(&apic->lapic_timer.timer,
                      ktime_add_ns(now, apic->lapic_timer.period),
                      HRTIMER_MODE_ABS);
}
读取客户机操作系统设置间隔时间,如果没有设置,不调用回调函数。客户机设置&#20540;不能太小话,启动主机定时器,按照设置周期,调用回调函数

3。调用回调函数,产生中断
static int __kvm_timer_fn(struct kvm_vcpu *vcpu, struct kvm_timer *ktimer)
{
        int restart_timer = 0;
        wait_queue_head_t *q = &vcpu->wq;

        /*
         * There is a race window between reading and incrementing, but we do
         * not care about potentially loosing timer events in the !reinject
         * case anyway.
         */
        if (ktimer->reinject || !atomic_read(&ktimer->pending)) {
                atomic_inc(&ktimer->pending);
                /* FIXME: this code should not know anything about vcpus */
                set_bit(KVM_REQ_PENDING_TIMER, &vcpu->requests);
        }
        
        if (waitqueue_active(q))
                wake_up_interruptible(q);

        if (ktimer->t_ops->is_periodic(ktimer)) {
                hrtimer_add_expires_ns(&ktimer->timer, ktimer->period);
                restart_timer = 1;
        }
               
        return restart_timer;
}
执行atomic_inc(&ktimer->pending)该函数,相当与产生中断。(如果有pending中断,如何不增加计数了吗?还有一个条件要重新注入,注入不成功时,哪些情况造成注入不成功呢)
查看目前等待队列是否为空,不为空,唤醒相应进程,以便下次调度执行相应进程。
如果APIC Timer设备是周期性,再次添加定时器,再次调用回调函数。

4.时钟中断注入

static int __vcpu_run(struct kvm_vcpu *vcpu)
{
   clear_bit(KVM_REQ_PENDING_TIMER, &vcpu->requests);
                if (kvm_cpu_has_pending_timer(vcpu))
                        kvm_inject_pending_timer_irqs(vcpu);
}
/*
* check if there are pending timer events
* to be processed.
*/
int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
{                                             
        int ret;

        ret = pit_has_pending_timer(vcpu);
        ret |= apic_has_pending_timer(vcpu);

        return ret;
}
下面这两个判断条件 (atomic_read(&lapic->lapic_timer.pending)好像重复,其实不必再判断了。
int apic_has_pending_timer(struct kvm_vcpu *vcpu)
{
        struct kvm_lapic *lapic = vcpu->arch.apic;

        if (lapic && apic_enabled(lapic) && apic_lvt_enabled(lapic, APIC_LVTT))
                return atomic_read(&lapic->lapic_timer.pending);

        return 0;
}
读取产生中断,传递中断,成功后清空悬挂中断请求。
void kvm_inject_apic_timer_irqs(struct kvm_vcpu *vcpu)
{      
        struct kvm_lapic *apic = vcpu->arch.apic;

        if (apic && atomic_read(&apic->lapic_timer.pending) > 0) {
                if (kvm_apic_local_deliver(apic, APIC_LVTT))
                        atomic_dec(&apic->lapic_timer.pending);
        }               
}

传递中断到lapci相应功能单元,__apic_accept_irq函数在《qemu-kvm中断虚拟化已分析》
static int kvm_apic_local_deliver(struct kvm_lapic *apic, int lvt_type)
{                       
        u32 reg = apic_get_reg(apic, lvt_type);
        int vector, mode, trig_mode;

        if (apic_hw_enabled(apic) && !(reg & APIC_LVT_MASKED)) {
                vector = reg & APIC_VECTOR_MASK;
                mode = reg & APIC_MODE_MASK;
                trig_mode = reg & APIC_LVT_LEVEL_TRIGGER;
                return __apic_accept_irq(apic, mode, vector, 1, trig_mode);
        }
        return 0;
}

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-125093-1-1.html 上篇帖子: [镜像制作]VMWare搭建Openstack——KVM制作虚拟机镜像 下篇帖子: centos7下安装KVM虚拟机
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表