mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
Backtrack on the new interface for reserved event-channel
ports, as binding them in user space via the evtchn driver would be a pain. Instead extend VIRQs so they can be classified as 'global' or 'per vcpu'. The former can only be allocated once per guest, but can be re-bound to an arbitrary VCPU. Signed-off-by: Keir Fraser <keir@xensource.com>
This commit is contained in:
1 parent
b0a4d66a6c
commit
6f1d9ff919
9 files changed
+100
-68
No files matched your search
@@ -274,6 +274,6 @@ int reprogram_timer(s_time_t timeout)
|
||||
|
||||
void send_timer_event(struct vcpu *v)
|
||||
{
|
||||
send_guest_virq(v, VIRQ_TIMER);
|
||||
send_guest_vcpu_virq(v, VIRQ_TIMER);
|
||||
}
|
||||
|
||||
+1
-1
@@ -916,7 +916,7 @@ void __init early_time_init(void)
|
||||
|
||||
void send_timer_event(struct vcpu *v)
|
||||
{
|
||||
send_guest_virq(v, VIRQ_TIMER);
|
||||
send_guest_vcpu_virq(v, VIRQ_TIMER);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+4
-4
@@ -137,7 +137,7 @@ void domain_kill(struct domain *d)
|
||||
domain_relinquish_resources(d);
|
||||
put_domain(d);
|
||||
|
||||
send_guest_virq(dom0->vcpu[0], VIRQ_DOM_EXC);
|
||||
send_guest_global_virq(dom0, VIRQ_DOM_EXC);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ static void domain_shutdown_finalise(void)
|
||||
|
||||
/* Don't set DOMF_shutdown until execution contexts are sync'ed. */
|
||||
if ( !test_and_set_bit(_DOMF_shutdown, &d->domain_flags) )
|
||||
send_guest_virq(dom0->vcpu[0], VIRQ_DOM_EXC);
|
||||
send_guest_global_virq(dom0, VIRQ_DOM_EXC);
|
||||
|
||||
UNLOCK_BIGLOCK(d);
|
||||
|
||||
@@ -267,7 +267,7 @@ void domain_pause_for_debugger(void)
|
||||
for_each_vcpu ( d, v )
|
||||
vcpu_sleep_nosync(v);
|
||||
|
||||
send_guest_virq(dom0->vcpu[0], VIRQ_DEBUGGER);
|
||||
send_guest_global_virq(dom0, VIRQ_DEBUGGER);
|
||||
}
|
||||
|
||||
|
||||
@@ -307,7 +307,7 @@ void domain_destroy(struct domain *d)
|
||||
|
||||
free_domain(d);
|
||||
|
||||
send_guest_virq(dom0->vcpu[0], VIRQ_DOM_EXC);
|
||||
send_guest_global_virq(dom0, VIRQ_DOM_EXC);
|
||||
}
|
||||
|
||||
void vcpu_pause(struct vcpu *v)
|
||||
|
||||
+60
-41
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Event notifications from VIRQs, PIRQs, and other domains.
|
||||
*
|
||||
* Copyright (c) 2003-2005, K A Fraser.
|
||||
* Copyright (c) 2003-2006, K A Fraser.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -47,6 +47,27 @@
|
||||
} while ( 0 )
|
||||
|
||||
|
||||
static int virq_is_global(int virq)
|
||||
{
|
||||
int rc;
|
||||
|
||||
ASSERT((virq >= 0) && (virq < NR_VIRQS));
|
||||
|
||||
switch ( virq )
|
||||
{
|
||||
case VIRQ_TIMER:
|
||||
case VIRQ_DEBUG:
|
||||
rc = 0;
|
||||
break;
|
||||
default:
|
||||
rc = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int get_free_port(struct domain *d)
|
||||
{
|
||||
struct evtchn *chn;
|
||||
@@ -182,6 +203,9 @@ static long evtchn_bind_virq(evtchn_bind_virq_t *bind)
|
||||
if ( virq >= ARRAY_SIZE(v->virq_to_evtchn) )
|
||||
return -EINVAL;
|
||||
|
||||
if ( virq_is_global(virq) && (vcpu != 0) )
|
||||
return -EINVAL;
|
||||
|
||||
if ( (vcpu >= ARRAY_SIZE(d->vcpu)) || ((v = d->vcpu[vcpu]) == NULL) )
|
||||
return -ENOENT;
|
||||
|
||||
@@ -474,19 +498,43 @@ void evtchn_set_pending(struct vcpu *v, int port)
|
||||
}
|
||||
|
||||
|
||||
void send_guest_virq(struct vcpu *v, int virq)
|
||||
void send_guest_vcpu_virq(struct vcpu *v, int virq)
|
||||
{
|
||||
int port = v->virq_to_evtchn[virq];
|
||||
int port;
|
||||
|
||||
if ( likely(port != 0) )
|
||||
evtchn_set_pending(v, port);
|
||||
ASSERT(!virq_is_global(virq));
|
||||
|
||||
port = v->virq_to_evtchn[virq];
|
||||
if ( unlikely(port == 0) )
|
||||
return;
|
||||
|
||||
evtchn_set_pending(v, port);
|
||||
}
|
||||
|
||||
void send_guest_global_virq(struct domain *d, int virq)
|
||||
{
|
||||
int port;
|
||||
struct evtchn *chn;
|
||||
|
||||
ASSERT(virq_is_global(virq));
|
||||
|
||||
port = d->vcpu[0]->virq_to_evtchn[virq];
|
||||
if ( unlikely(port == 0) )
|
||||
return;
|
||||
|
||||
chn = evtchn_from_port(d, port);
|
||||
evtchn_set_pending(d->vcpu[chn->notify_vcpu_id], port);
|
||||
}
|
||||
|
||||
|
||||
void send_guest_pirq(struct domain *d, int pirq)
|
||||
{
|
||||
int port = d->pirq_to_evtchn[pirq];
|
||||
struct evtchn *chn = evtchn_from_port(d, port);
|
||||
struct evtchn *chn;
|
||||
|
||||
ASSERT(port != 0);
|
||||
|
||||
chn = evtchn_from_port(d, port);
|
||||
evtchn_set_pending(d->vcpu[chn->notify_vcpu_id], port);
|
||||
}
|
||||
|
||||
@@ -576,6 +624,12 @@ long evtchn_bind_vcpu(unsigned int port, unsigned int vcpu_id)
|
||||
chn = evtchn_from_port(d, port);
|
||||
switch ( chn->state )
|
||||
{
|
||||
case ECS_VIRQ:
|
||||
if ( virq_is_global(chn->u.virq) )
|
||||
chn->notify_vcpu_id = vcpu_id;
|
||||
else
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
case ECS_UNBOUND:
|
||||
case ECS_INTERDOMAIN:
|
||||
case ECS_PIRQ:
|
||||
@@ -702,41 +756,6 @@ long do_event_channel_op(GUEST_HANDLE(evtchn_op_t) uop)
|
||||
}
|
||||
|
||||
|
||||
int evtchn_open_reserved_port(struct domain *d)
|
||||
{
|
||||
struct evtchn *chn;
|
||||
int port;
|
||||
|
||||
spin_lock(&d->evtchn_lock);
|
||||
|
||||
if ( (port = get_free_port(d)) >= 0 )
|
||||
{
|
||||
chn = evtchn_from_port(d, port);
|
||||
chn->state = ECS_RESERVED;
|
||||
}
|
||||
|
||||
spin_unlock(&d->evtchn_lock);
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
|
||||
void evtchn_close_reserved_port(struct domain *d, int port)
|
||||
{
|
||||
struct evtchn *chn;
|
||||
|
||||
spin_lock(&d->evtchn_lock);
|
||||
|
||||
BUG_ON(!port_is_valid(d, port));
|
||||
|
||||
chn = evtchn_from_port(d, port);
|
||||
chn->state = ECS_FREE;
|
||||
chn->notify_vcpu_id = 0;
|
||||
|
||||
spin_unlock(&d->evtchn_lock);
|
||||
}
|
||||
|
||||
|
||||
void evtchn_notify_reserved_port(struct domain *d, int port)
|
||||
{
|
||||
struct evtchn *chn = evtchn_from_port(d, port);
|
||||
|
||||
@@ -162,7 +162,7 @@ static void dump_domains(unsigned char key)
|
||||
&d->shared_info->evtchn_mask[0]),
|
||||
test_bit(v->virq_to_evtchn[VIRQ_DEBUG]/BITS_PER_LONG,
|
||||
&v->vcpu_info->evtchn_pending_sel));
|
||||
send_guest_virq(v, VIRQ_DEBUG);
|
||||
send_guest_vcpu_virq(v, VIRQ_DEBUG);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -294,7 +294,7 @@ static void __serial_rx(char c, struct cpu_user_regs *regs)
|
||||
if ( (serial_rx_prod-serial_rx_cons) != SERIAL_RX_SIZE )
|
||||
serial_rx_ring[SERIAL_RX_MASK(serial_rx_prod++)] = c;
|
||||
/* Always notify the guest: prevents receive path from getting stuck. */
|
||||
send_guest_virq(dom0->vcpu[0], VIRQ_CONSOLE);
|
||||
send_guest_global_virq(dom0, VIRQ_CONSOLE);
|
||||
}
|
||||
|
||||
static void serial_rx(char c, struct cpu_user_regs *regs)
|
||||
|
||||
@@ -50,9 +50,13 @@ typedef struct evtchn_bind_interdomain {
|
||||
* EVTCHNOP_bind_virq: Bind a local event channel to VIRQ <irq> on specified
|
||||
* vcpu.
|
||||
* NOTES:
|
||||
* 1. A virtual IRQ may be bound to at most one event channel per vcpu.
|
||||
* 2. The allocated event channel is bound to the specified vcpu. The binding
|
||||
* may not be changed.
|
||||
* 1. Virtual IRQs are classified as per-vcpu or global. See the VIRQ list
|
||||
* in xen.h for the classification of each VIRQ.
|
||||
* 2. Global VIRQs must be allocated on VCPU0 but can subsequently be
|
||||
* re-bound via EVTCHNOP_bind_vcpu.
|
||||
* 3. Per-vcpu VIRQs may be bound to at most one event channel per vcpu.
|
||||
* The allocated event channel is bound to the specified vcpu and the
|
||||
* binding cannot be changed.
|
||||
*/
|
||||
#define EVTCHNOP_bind_virq 1
|
||||
typedef struct evtchn_bind_virq {
|
||||
@@ -152,9 +156,11 @@ typedef struct evtchn_status {
|
||||
* EVTCHNOP_bind_vcpu: Specify which vcpu a channel should notify when an
|
||||
* event is pending.
|
||||
* NOTES:
|
||||
* 1. IPI- and VIRQ-bound channels always notify the vcpu that initialised
|
||||
* the binding. This binding cannot be changed.
|
||||
* 2. All other channels notify vcpu0 by default. This default is set when
|
||||
* 1. IPI-bound channels always notify the vcpu specified at bind time.
|
||||
* This binding cannot be changed.
|
||||
* 2. Per-VCPU VIRQ channels always notify the vcpu specified at bind time.
|
||||
* This binding cannot be changed.
|
||||
* 3. All other channels notify vcpu0 by default. This default is set when
|
||||
* the channel is allocated (a port that is freed and subsequently reused
|
||||
* has its binding reset to vcpu0).
|
||||
*/
|
||||
|
||||
@@ -65,12 +65,17 @@
|
||||
* VIRTUAL INTERRUPTS
|
||||
*
|
||||
* Virtual interrupts that a guest OS may receive from Xen.
|
||||
*
|
||||
* In the side comments, 'V.' denotes a per-VCPU VIRQ while 'G.' denotes a
|
||||
* global VIRQ. The former can be bound once per VCPU and cannot be re-bound.
|
||||
* The latter can be allocated only once per guest: they must initially be
|
||||
* allocated to VCPU0 but can subsequently be re-bound.
|
||||
*/
|
||||
#define VIRQ_TIMER 0 /* Timebase update, and/or requested timeout. */
|
||||
#define VIRQ_DEBUG 1 /* Request guest to dump debug info. */
|
||||
#define VIRQ_CONSOLE 2 /* (DOM0) Bytes received on emergency console. */
|
||||
#define VIRQ_DOM_EXC 3 /* (DOM0) Exceptional event for some domain. */
|
||||
#define VIRQ_DEBUGGER 6 /* (DOM0) A domain has paused for debugging. */
|
||||
#define VIRQ_TIMER 0 /* V. Timebase update, and/or requested timeout. */
|
||||
#define VIRQ_DEBUG 1 /* V. Request guest to dump debug info. */
|
||||
#define VIRQ_CONSOLE 2 /* G. (DOM0) Bytes received on emergency console. */
|
||||
#define VIRQ_DOM_EXC 3 /* G. (DOM0) Exceptional event for some domain. */
|
||||
#define VIRQ_DEBUGGER 6 /* G. (DOM0) A domain has paused for debugging. */
|
||||
#define NR_VIRQS 8
|
||||
|
||||
/*
|
||||
|
||||
+10
-8
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* A nice interface for passing asynchronous events to guest OSes.
|
||||
*
|
||||
* Copyright (c) 2002-2005, K A Fraser
|
||||
* Copyright (c) 2002-2006, K A Fraser
|
||||
*/
|
||||
|
||||
#ifndef __XEN_EVENT_H__
|
||||
@@ -18,11 +18,18 @@
|
||||
extern void evtchn_set_pending(struct vcpu *v, int port);
|
||||
|
||||
/*
|
||||
* send_guest_virq:
|
||||
* send_guest_vcpu_virq: Notify guest via a per-VCPU VIRQ.
|
||||
* @v: VCPU to which virtual IRQ should be sent
|
||||
* @virq: Virtual IRQ number (VIRQ_*)
|
||||
*/
|
||||
extern void send_guest_virq(struct vcpu *v, int virq);
|
||||
extern void send_guest_vcpu_virq(struct vcpu *v, int virq);
|
||||
|
||||
/*
|
||||
* send_guest_global_virq: Notify guest via a global VIRQ.
|
||||
* @d: Domain to which virtual IRQ should be sent
|
||||
* @virq: Virtual IRQ number (VIRQ_*)
|
||||
*/
|
||||
extern void send_guest_global_virq(struct domain *d, int virq);
|
||||
|
||||
/*
|
||||
* send_guest_pirq:
|
||||
@@ -45,9 +52,4 @@ extern long evtchn_send(unsigned int lport);
|
||||
/* Bind a local event-channel port to the specified VCPU. */
|
||||
extern long evtchn_bind_vcpu(unsigned int port, unsigned int vcpu_id);
|
||||
|
||||
/* Reserved event-channel ports for other Xen subsystems. */
|
||||
int evtchn_open_reserved_port(struct domain *d);
|
||||
void evtchn_close_reserved_port(struct domain *d, int port);
|
||||
void evtchn_notify_reserved_port(struct domain *d, int port);
|
||||
|
||||
#endif /* __XEN_EVENT_H__ */
|
||||