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

[经验分享] Xen Event Channel (2)

[复制链接]

尚未签到

发表于 2015-10-12 07:32:05 | 显示全部楼层 |阅读模式
Xen Event Channel (2) static long evtchn_bind_ipi(evtchn_bind_ipi_t *bind)
{
    struct evtchn *chn;
    struct domain *d = current->domain;                            // 当前dom
    int            port, vcpu = bind->vcpu;
    long           rc = 0;

    if ( (vcpu < 0) || (vcpu >= d->max_vcpus) ||
         (d->vcpu[vcpu] == NULL) )
        return -ENOENT;

    spin_lock(&d->event_lock);

    if ( (port = get_free_port(d)) < 0 )                                     // 获得一个port
        ERROR_EXIT(port);

    chn = evtchn_from_port(d, port);                                     // 获得port对应的evtchn
    chn->state          = ECS_IPI;
    chn->notify_vcpu_id = vcpu;                                              // evtchnIPI绑定,即设置此evtchnnotify_vcpu_id

    bind->port = port;                                                               // 设置输出参数

out:
    spin_unlock(&d->event_lock);

    return rc;
}

static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind)
{
    struct evtchn *chn;
    struct domain *d = current->domain;
    int            port, pirq = bind->pirq;
    long           rc;

    if ( (pirq < 0) || (pirq >= d->nr_pirqs) )
        return -EINVAL;

    if ( !irq_access_permitted(d, pirq) )
        return -EPERM;

    spin_lock(&d->event_lock);

    if ( d->pirq_to_evtchn[pirq] != 0 )
        ERROR_EXIT(-EEXIST);

    if ( (port = get_free_port(d)) < 0 )                                     // 分配一个freeport
        ERROR_EXIT(port);

    chn = evtchn_from_port(d, port);                                     // 获得对应的evtchn

    d->pirq_to_evtchn[pirq] = port;                                       // PIRQ绑定的port设置到dompirq_to_evtchn
    rc = pirq_guest_bind(d->vcpu[0], pirq,
                         !!(bind->flags & BIND_PIRQ__WILL_SHARE));
    if ( rc != 0 )
    {
        d->pirq_to_evtchn[pirq] = 0;
        goto out;
    }

    chn->state  = ECS_PIRQ;
    chn->u.pirq = pirq;                                                              // PIRQ绑定

    bind->port = port;                                                               // 设置输出参数

out:
    spin_unlock(&d->event_lock);

    return rc;
}

static long __evtchn_close(struct domain *d1, int port1)
{
    struct domain *d2 = NULL;
    struct vcpu   *v;
    struct evtchn *chn1, *chn2;
    int            port2;
    long           rc = 0;

again:
    spin_lock(&d1->event_lock);

    if ( !port_is_valid(d1, port1) )
    {
        rc = -EINVAL;
        goto out;
    }

    chn1 = evtchn_from_port(d1, port1);                                                 // 得到对应的evtchn

    /* Guest cannot close a Xen-attached event channel. */
    if ( unlikely(chn1->consumer_is_xen) )
    {
        rc = -EINVAL;
        goto out;
    }

    switch ( chn1->state )
    {
    case ECS_FREE:
    case ECS_RESERVED:
        rc = -EINVAL;
        goto out;

    case ECS_UNBOUND:
        break;

    case ECS_PIRQ:
        pirq_guest_unbind(d1, chn1->u.pirq);
        d1->pirq_to_evtchn[chn1->u.pirq] = 0;
        break;

    case ECS_VIRQ:
        for_each_vcpu ( d1, v )
        {
            if ( v->virq_to_evtchn[chn1->u.virq] != port1 )
                continue;
            v->virq_to_evtchn[chn1->u.virq] = 0;
            spin_barrier_irq(&v->virq_lock);
        }
        break;

    case ECS_IPI:
        break;

    case ECS_INTERDOMAIN:
        if ( d2 == NULL )
        {
            d2 = chn1->u.interdomain.remote_dom;

            /* If we unlock d1 then we could lose d2. Must get a reference. */
            if ( unlikely(!get_domain(d2)) )
                BUG();

            if ( d1 < d2 )
            {
                spin_lock(&d2->event_lock);
            }
            else if ( d1 != d2 )
            {
                spin_unlock(&d1->event_lock);
                spin_lock(&d2->event_lock);
                goto again;
            }
        }
        else if ( d2 != chn1->u.interdomain.remote_dom )
        {
            /*
             * We can only get here if the port was closed and re-bound after
             * unlocking d1 but before locking d2 above. We could retry but
             * it is easier to return the same error as if we had seen the
             * port in ECS_CLOSED. It must have passed through that state for
             * us to end up here, so it's a valid error to return.
             */
            rc = -EINVAL;
            goto out;
        }

        port2 = chn1->u.interdomain.remote_port;                             // 获得远端port
        BUG_ON(!port_is_valid(d2, port2));

        chn2 = evtchn_from_port(d2, port2);                                         // 获得对应的evtchn
        BUG_ON(chn2->state != ECS_INTERDOMAIN);
        BUG_ON(chn2->u.interdomain.remote_dom != d1);

        chn2->state = ECS_UNBOUND;                                               //设置远端为alloc之后,绑定之前的状态
        chn2->u.unbound.remote_domid = d1->domain_id;              // ECS_UNBOUND
        break;

    default:
        BUG();
    }

    /* Clear pending event to avoid unexpected behavior on re-bind. */
    clear_bit(port1, &shared_info(d1, evtchn_pending));

    /* Reset binding to vcpu0 when the channel is freed. */
    chn1->state          = ECS_FREE;                                               // 设置本地为ECS_FREE状态
    chn1->notify_vcpu_id = 0;                                                                    // 设置初始化状态绑定的VCPU0

    xsm_evtchn_close_post(chn1);

out:
    if ( d2 != NULL )
    {
        if ( d1 != d2 )
            spin_unlock(&d2->event_lock);
        put_domain(d2);
    }

    spin_unlock(&d1->event_lock);

    return rc;
}


static long evtchn_close(evtchn_close_t *close)
{
    return __evtchn_close(current->domain, close->port);
}

int evtchn_send(struct domain *d, unsigned int lport)
d为本地dom。是current->domain
lport为本地port,要send的对象
{
    struct evtchn *lchn, *rchn;
    struct domain *ld = d, *rd;
    struct vcpu   *rvcpu;
    int            rport, ret = 0;

    spin_lock(&ld->event_lock);

    if ( unlikely(!port_is_valid(ld, lport)) )
    {
        spin_unlock(&ld->event_lock);
        return -EINVAL;
    }

    lchn = evtchn_from_port(ld, lport);                // 首先获得lport对应的本地evtchn

    /* Guest cannot send via a Xen-attached event channel. */
    if ( unlikely(lchn->consumer_is_xen) )
    {
        spin_unlock(&ld->event_lock);
        return -EINVAL;
    }

    ret = xsm_evtchn_send(ld, lchn);
    if ( ret )
        goto out;

    switch ( lchn->state )
    {
    case ECS_INTERDOMAIN:                                             // 域间通信
        rd    = lchn->u.interdomain.remote_dom;          // 获得远端/对点的rdom
        rport = lchn->u.interdomain.remote_port;            // 获得远端/对点的rport
        rchn  = evtchn_from_port(rd, rport);                     // 获得rport对应的rchn
        rvcpu = rd->vcpu[rchn->notify_vcpu_id];                // 获得对点evtchn绑定的VCPU
        if ( rchn->consumer_is_xen )
        {
            /* Xen consumers need notification only if they are blocked. */
            if ( test_and_clear_bit(_VPF_blocked_in_xen,
                                    &rvcpu->pause_flags) )
                vcpu_wake(rvcpu);
        }
        else
        {
            evtchn_set_pending(rvcpu, rport);      // 设置对点VCPU上的portevent发生。
        }                                                                                       // 下面将异步进行event句柄的处理。
        break;
    case ECS_IPI:
       evtchn_set_pending(ld->vcpu[lchn->notify_vcpu_id], lport);                  // IPI的话,则设置对应的VCPU
        break;
    case ECS_UNBOUND:
        /* silently drop the notification */
        break;
    default:                                                               // ESC_VIRQ & ESC_PIRQ不会到这里来
        ret = -EINVAL;                                         // 因为这些notification是的发送方是Xen,不需要用hypercall
    }

out:
    spin_unlock(&ld->event_lock);

    return ret;
}
long evtchn_bind_vcpu(unsigned int port, unsigned int vcpu_id)

{
绑定VCPU之后,这个event的处理就由该VCPU来完成。
evtchn的屏蔽:
1)   所有的VCPU屏蔽某个evtchn
    设置evtchnMASK位。这个MASK位在struct shared_info里面。
2)   某个VCPU屏蔽所有evtchn
    VCPU结构体struct vcpu_info成员完成。方法在结构体处说明。
    struct domain *d = current->domain;
    struct evtchn *chn;
    long           rc = 0;

    if ( (vcpu_id >= d->max_vcpus) || (d->vcpu[vcpu_id] == NULL) )
        return -ENOENT;

    spin_lock(&d->event_lock);

    if ( !port_is_valid(d, port) )
    {
        rc = -EINVAL;
        goto out;
    }

    chn = evtchn_from_port(d, port);                                                        // 根据port得到evtchn

    /* Guest cannot re-bind a Xen-attached event channel. */
    if ( unlikely(chn->consumer_is_xen) )
    {
        rc = -EINVAL;
        goto out;
    }

    switch ( chn->state )
    {
    case ECS_VIRQ:
        if ( virq_is_global(chn->u.virq) )                              // 只有全局性虚拟中断才能绑定VCPU
            chn->notify_vcpu_id = vcpu_id;     // 所谓绑定,不过是设置evtchn中的notify_vcpu_id
        else
            rc = -EINVAL;
        break;
    case ECS_UNBOUND:
    case ECS_INTERDOMAIN:        // 域间绑定之后,状态会被设置为ECS_INTERDOMAIN
    case ECS_PIRQ:
        chn->notify_vcpu_id = vcpu_id;
        break;
    default:
        rc = -EINVAL;
        break;
    }

out:
    spin_unlock(&d->event_lock);

    return rc;
}

int evtchn_unmask(unsigned int port)
{
    struct domain *d = current->domain;
    struct vcpu   *v;

    spin_lock(&d->event_lock);

    if ( unlikely(!port_is_valid(d, port)) )
    {
        spin_unlock(&d->event_lock);
        return -EINVAL;
    }

    v = d->vcpu[evtchn_from_port(d, port)->notify_vcpu_id];                     // 获得对应的VCPU

    /*
     * These operations must happen in strict order. Based on
     * include/xen/event.h:evtchn_set_pending().
     */
    if ( test_and_clear_bit(port, &shared_info(d, evtchn_mask)) && // 如果evtchn_mask被设置,那么取消设置(屏蔽)
         test_bit          (port, &shared_info(d, evtchn_pending)) && // 并且evtchn_pending被设置(未决)
         !test_and_set_bit (port / BITS_PER_EVTCHN_WORD(d),      // 那么设置此VCPU里的evtchn_pending_sel
                            &vcpu_info(v, evtchn_pending_sel)) )
    {
        vcpu_mark_events_pending(v);                                       // 并且设置此VCPU里的evtchn_upcall_pending
    }

    spin_unlock(&d->event_lock);

    return 0;
}

static long evtchn_reset(evtchn_reset_t *r)
{
    domid_t dom = r->dom;
    struct domain *d;
    int i, rc;

    rc = rcu_lock_target_domain_by_id(dom, &d);
    if ( rc )
        return rc;

    rc = xsm_evtchn_reset(current->domain, d);
    if ( rc )
        goto out;

    for ( i = 0; port_is_valid(d, i); i++ )
        (void)__evtchn_close(d, i);            // 可以这样做的原因是,当初分配port的时候就是严格按照顺序分配的

    rc = 0;

out:
    rcu_unlock_domain(d);

    return rc;
}

运维网声明 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-125579-1-1.html 上篇帖子: Xen 虚拟机架构 下篇帖子: Xen-network 学习笔记-2 (TechDiary-20110226)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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