xen/pv-on-hvm kexec: rebind virqs to existing eventchannel ports
During a kexec boot some virqs such as timer and debugirq were already registered by the old kernel. The hypervisor will return -EEXISTS from the new EVTCHNOP_bind_virq request and the BUG in bind_virq_to_irq() triggers. Catch the -EEXISTS error and loop through all possible ports to find what port belongs to the virq/cpu combo. Signed-off-by: Olaf Hering <olaf@aepfle.de> [v2: - use NR_EVENT_CHANNELS instead of private MAX_EVTCHNS] Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
This commit is contained in:
committed by
Konrad Rzeszutek Wilk
parent
c4c303c7c5
commit
62cc5fc7b2
@@ -877,11 +877,32 @@ static int bind_interdomain_evtchn_to_irq(unsigned int remote_domain,
|
|||||||
return err ? : bind_evtchn_to_irq(bind_interdomain.local_port);
|
return err ? : bind_evtchn_to_irq(bind_interdomain.local_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int find_virq(unsigned int virq, unsigned int cpu)
|
||||||
|
{
|
||||||
|
struct evtchn_status status;
|
||||||
|
int port, rc = -ENOENT;
|
||||||
|
|
||||||
|
memset(&status, 0, sizeof(status));
|
||||||
|
for (port = 0; port <= NR_EVENT_CHANNELS; port++) {
|
||||||
|
status.dom = DOMID_SELF;
|
||||||
|
status.port = port;
|
||||||
|
rc = HYPERVISOR_event_channel_op(EVTCHNOP_status, &status);
|
||||||
|
if (rc < 0)
|
||||||
|
continue;
|
||||||
|
if (status.status != EVTCHNSTAT_virq)
|
||||||
|
continue;
|
||||||
|
if (status.u.virq == virq && status.vcpu == cpu) {
|
||||||
|
rc = port;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
int bind_virq_to_irq(unsigned int virq, unsigned int cpu)
|
int bind_virq_to_irq(unsigned int virq, unsigned int cpu)
|
||||||
{
|
{
|
||||||
struct evtchn_bind_virq bind_virq;
|
struct evtchn_bind_virq bind_virq;
|
||||||
int evtchn, irq;
|
int evtchn, irq, ret;
|
||||||
|
|
||||||
spin_lock(&irq_mapping_update_lock);
|
spin_lock(&irq_mapping_update_lock);
|
||||||
|
|
||||||
@@ -897,10 +918,16 @@ int bind_virq_to_irq(unsigned int virq, unsigned int cpu)
|
|||||||
|
|
||||||
bind_virq.virq = virq;
|
bind_virq.virq = virq;
|
||||||
bind_virq.vcpu = cpu;
|
bind_virq.vcpu = cpu;
|
||||||
if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq,
|
ret = HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq,
|
||||||
&bind_virq) != 0)
|
&bind_virq);
|
||||||
BUG();
|
if (ret == 0)
|
||||||
evtchn = bind_virq.port;
|
evtchn = bind_virq.port;
|
||||||
|
else {
|
||||||
|
if (ret == -EEXIST)
|
||||||
|
ret = find_virq(virq, cpu);
|
||||||
|
BUG_ON(ret < 0);
|
||||||
|
evtchn = ret;
|
||||||
|
}
|
||||||
|
|
||||||
xen_irq_info_virq_init(cpu, irq, evtchn, virq);
|
xen_irq_info_virq_init(cpu, irq, evtchn, virq);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user