int evtchn_send(struct domain *d, unsigned int lport) { 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); /* Guest cannot send via a Xen-attached event channel. */ if ( unlikely(consumer_is_xen(lchn)) ) { 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; rport = lchn->u.interdomain.remote_port; rchn = evtchn_from_port(rd, rport); rvcpu = rd->vcpu[rchn->notify_vcpu_id]; if ( consumer_is_xen(rchn) ) (*xen_notification_fn(rchn))(rvcpu, rport); else evtchn_set_pending(rvcpu, rport); break; case ECS_IPI: evtchn_set_pending(ld->vcpu[lchn->notify_vcpu_id], lport); break; case ECS_UNBOUND: /* silently drop the notification */ break; default: ret = -EINVAL; } out: spin_unlock(&ld->event_lock); return ret; }
static void send_guest_global_virq(struct domain *d, uint32_t virq) { unsigned long flags; int port; struct vcpu *v; struct evtchn *chn; ASSERT(virq_is_global(virq)); if ( unlikely(d == NULL) || unlikely(d->vcpu == NULL) ) return; v = d->vcpu[0]; if ( unlikely(v == NULL) ) return; spin_lock_irqsave(&v->virq_lock, flags); port = v->virq_to_evtchn[virq]; if ( unlikely(port == 0) ) goto out; chn = evtchn_from_port(d, port); evtchn_set_pending(d->vcpu[chn->notify_vcpu_id], port); out: spin_unlock_irqrestore(&v->virq_lock, flags); }
void send_guest_vcpu_virq(struct vcpu *v, uint32_t virq) { unsigned long flags; int port; ASSERT(!virq_is_global(virq)); spin_lock_irqsave(&v->virq_lock, flags); port = v->virq_to_evtchn[virq]; if ( unlikely(port == 0) ) goto out; evtchn_set_pending(v, port); out: spin_unlock_irqrestore(&v->virq_lock, flags); }
void send_guest_pirq(struct domain *d, const struct pirq *pirq) { int port; struct evtchn *chn; /* * PV guests: It should not be possible to race with __evtchn_close(). The * caller of this function must synchronise with pirq_guest_unbind(). * HVM guests: Port is legitimately zero when the guest disables the * emulated interrupt/evtchn. */ if ( pirq == NULL || (port = pirq->evtchn) == 0 ) { BUG_ON(!is_hvm_domain(d)); return; } chn = evtchn_from_port(d, port); evtchn_set_pending(d->vcpu[chn->notify_vcpu_id], port); }
long evtchn_send(unsigned int lport) { struct evtchn *lchn, *rchn; struct domain *ld = current->domain, *rd; struct vcpu *rvcpu; int rport, ret = 0; spin_lock(&ld->evtchn_lock); if ( unlikely(!port_is_valid(ld, lport)) ) { spin_unlock(&ld->evtchn_lock); return -EINVAL; } lchn = evtchn_from_port(ld, lport); /* Guest cannot send via a Xen-attached event channel. */ if ( unlikely(lchn->consumer_is_xen) ) { spin_unlock(&ld->evtchn_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; rport = lchn->u.interdomain.remote_port; rchn = evtchn_from_port(rd, rport); rvcpu = rd->vcpu[rchn->notify_vcpu_id]; 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); } break; case ECS_IPI: evtchn_set_pending(ld->vcpu[lchn->notify_vcpu_id], lport); break; case ECS_UNBOUND: /* silently drop the notification */ break; default: ret = -EINVAL; } out: spin_unlock(&ld->evtchn_lock); return ret; }
static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) { struct evtchn *lchn, *rchn; struct domain *ld = current->domain, *rd; int lport, rport = bind->remote_port; domid_t rdom = bind->remote_dom; long rc; if ( rdom == DOMID_SELF ) rdom = current->domain->domain_id; if ( (rd = rcu_lock_domain_by_id(rdom)) == NULL ) return -ESRCH; /* Avoid deadlock by first acquiring lock of domain with smaller id. */ if ( ld < rd ) { spin_lock(&ld->evtchn_lock); spin_lock(&rd->evtchn_lock); } else { if ( ld != rd ) spin_lock(&rd->evtchn_lock); spin_lock(&ld->evtchn_lock); } if ( (lport = get_free_port(ld)) < 0 ) ERROR_EXIT(lport); lchn = evtchn_from_port(ld, lport); if ( !port_is_valid(rd, rport) ) ERROR_EXIT_DOM(-EINVAL, rd); rchn = evtchn_from_port(rd, rport); if ( (rchn->state != ECS_UNBOUND) || (rchn->u.unbound.remote_domid != ld->domain_id) ) ERROR_EXIT_DOM(-EINVAL, rd); rc = xsm_evtchn_interdomain(ld, lchn, rd, rchn); if ( rc ) goto out; lchn->u.interdomain.remote_dom = rd; lchn->u.interdomain.remote_port = (u16)rport; lchn->state = ECS_INTERDOMAIN; rchn->u.interdomain.remote_dom = ld; rchn->u.interdomain.remote_port = (u16)lport; rchn->state = ECS_INTERDOMAIN; /* * We may have lost notifications on the remote unbound port. Fix that up * here by conservatively always setting a notification on the local port. */ evtchn_set_pending(ld->vcpu[lchn->notify_vcpu_id], lport); bind->local_port = lport; out: spin_unlock(&ld->evtchn_lock); if ( ld != rd ) spin_unlock(&rd->evtchn_lock); rcu_unlock_domain(rd); return rc; }