NetBSD-5.0.2/sys/arch/xen/xen/ctrl_if.c

Compare this file to the similar file:
Show the results in this format:

/*	$NetBSD: ctrl_if.c,v 1.19 2008/10/21 15:46:32 cegger Exp $	*/

/******************************************************************************
 * ctrl_if.c
 * 
 * Management functions for special interface to the domain controller.
 * 
 * Copyright (c) 2004, K A Fraser
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ctrl_if.c,v 1.19 2008/10/21 15:46:32 cegger Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/kthread.h>
#include <sys/malloc.h>
#include <sys/simplelock.h>

#include <xen/xen.h>
#include <xen/hypervisor.h>
#include <xen/ctrl_if.h>
#include <xen/evtchn.h>

#if 0
#define DPRINTK(_f, _a...) printk("(file=%s, line=%d) " _f, \
                           __FILE__ , __LINE__ , ## _a )
#else
#define DPRINTK(_f, _a...) ((void)0)
#endif

/*
 * Only used by initial domain which must create its own control-interface
 * event channel. This value is picked up by the user-space domain controller
 * via an ioctl.
 */
int initdom_ctrlif_domcontroller_port = -1;

/* static */ int ctrl_if_evtchn = -1;
static struct simplelock ctrl_if_lock;

static CONTROL_RING_IDX ctrl_if_tx_resp_cons;
static CONTROL_RING_IDX ctrl_if_rx_req_cons;

/* Incoming message requests. */
    /* Primary message type -> message handler. */
static ctrl_msg_handler_t ctrl_if_rxmsg_handler[256];
    /* Primary message type -> callback in process context? */
static unsigned long ctrl_if_rxmsg_blocking_context[256/sizeof(unsigned long)];
    /* Queue up messages to be handled in process context. */
static ctrl_msg_t ctrl_if_rxmsg_deferred[CONTROL_RING_SIZE];
static CONTROL_RING_IDX ctrl_if_rxmsg_deferred_prod;
static CONTROL_RING_IDX ctrl_if_rxmsg_deferred_cons;

/* Incoming message responses: message identifier -> message handler/id. */
static struct {
    ctrl_msg_handler_t fn;
    unsigned long      id;
} ctrl_if_txmsg_id_mapping[CONTROL_RING_SIZE];

/* For received messages that must be deferred to process context. */
static void __ctrl_if_rxmsg_deferred(void *unused);

static int ctrl_if_tx_wait;
static void __ctrl_if_tx_tasklet(unsigned long data);

static void __ctrl_if_rx_tasklet(unsigned long data);

#define get_ctrl_if() ((control_if_t *)((uintptr_t)HYPERVISOR_shared_info + 2048))
#define TX_FULL(_c)   \
    (((_c)->tx_req_prod - ctrl_if_tx_resp_cons) == CONTROL_RING_SIZE)

static void ctrl_if_notify_controller(void)
{
    hypervisor_notify_via_evtchn(ctrl_if_evtchn);
}

static void ctrl_if_rxmsg_default_handler(ctrl_msg_t *msg, unsigned long id)
{
    msg->length = 0;
    ctrl_if_send_response(msg);
}

static void
__ctrl_if_tx_tasklet(unsigned long data)
{
    control_if_t *ctrl_if = get_ctrl_if();
    ctrl_msg_t   *msg;
    int           was_full = TX_FULL(ctrl_if);
    CONTROL_RING_IDX rp;

    rp = ctrl_if->tx_resp_prod;
    x86_lfence(); /* Ensure we see all requests up to 'rp'. */

    while ( ctrl_if_tx_resp_cons != rp )
    {
        msg = &ctrl_if->tx_ring[MASK_CONTROL_IDX(ctrl_if_tx_resp_cons)];

        DPRINTK("Rx-Rsp %u/%u :: %d/%d\n", 
                ctrl_if_tx_resp_cons,
                ctrl_if->tx_resp_prod,
                msg->type, msg->subtype);

        /* Execute the callback handler, if one was specified. */
        if ( msg->id != 0xFF )
        {
            (*ctrl_if_txmsg_id_mapping[msg->id].fn)(
                msg, ctrl_if_txmsg_id_mapping[msg->id].id);
            __insn_barrier(); /* Execute, /then/ free. */
            ctrl_if_txmsg_id_mapping[msg->id].fn = NULL;
        }

        /*
         * Step over the message in the ring /after/ finishing reading it. As 
         * soon as the index is updated then the message may get blown away.
         */
        __insn_barrier();
        ctrl_if_tx_resp_cons++;
    }

    if ( was_full && !TX_FULL(ctrl_if) )
    {
        wakeup(&ctrl_if_tx_wait);
    }
}

static void
__ctrl_if_rxmsg_deferred(void *unused)
{
	ctrl_msg_t *msg;
	CONTROL_RING_IDX dp;
	int s;


	while (1) {
		s = splsoftnet();
		dp = ctrl_if_rxmsg_deferred_prod;
		x86_lfence(); /* Ensure we see all requests up to 'dp'. */
		if (ctrl_if_rxmsg_deferred_cons == dp) {
			tsleep(&ctrl_if_rxmsg_deferred_cons, PRIBIO,
			    "rxdef", 0);
			splx(s);
			continue;
		}
		splx(s);

		while ( ctrl_if_rxmsg_deferred_cons != dp )
		{
			msg = &ctrl_if_rxmsg_deferred[
			    MASK_CONTROL_IDX(ctrl_if_rxmsg_deferred_cons)];
			(*ctrl_if_rxmsg_handler[msg->type])(msg, 0);
			ctrl_if_rxmsg_deferred_cons++;
		}
	}
}

static void
__ctrl_if_rx_tasklet(unsigned long data)
{
    control_if_t *ctrl_if = get_ctrl_if();
    ctrl_msg_t    msg, *pmsg;
    CONTROL_RING_IDX rp, dp;

    dp = ctrl_if_rxmsg_deferred_prod;
    rp = ctrl_if->rx_req_prod;
    x86_lfence(); /* Ensure we see all requests up to 'rp'. */

    while ( ctrl_if_rx_req_cons != rp )
    {
        pmsg = &ctrl_if->rx_ring[MASK_CONTROL_IDX(ctrl_if_rx_req_cons)];
        memcpy(&msg, pmsg, offsetof(ctrl_msg_t, msg));

        DPRINTK("Rx-Req %u/%u :: %d/%d\n", 
                ctrl_if_rx_req_cons-1,
                ctrl_if->rx_req_prod,
                msg.type, msg.subtype);

        if ( msg.length != 0 )
            memcpy(msg.msg, pmsg->msg, msg.length);
	/*
	 * increase ctrl_if_rx_req_cons now,
	 * as the handler may end up calling __ctrl_if_rx_tasklet() again
	 * though the console polling code.
	 */
	ctrl_if_rx_req_cons++;
        if ( xen_atomic_test_bit(
                      (unsigned long *)&ctrl_if_rxmsg_blocking_context,
		      msg.type) )
            memcpy(&ctrl_if_rxmsg_deferred[MASK_CONTROL_IDX(dp++)],
                   &msg, offsetof(ctrl_msg_t, msg) + msg.length);
        else
            (*ctrl_if_rxmsg_handler[msg.type])(&msg, 0);
	/* update rp, in case the console polling code was used */
    	rp = ctrl_if->rx_req_prod;
    	x86_lfence(); /* Ensure we see all requests up to 'rp'. */
    }

    if ( dp != ctrl_if_rxmsg_deferred_prod )
    {
        __insn_barrier();
        ctrl_if_rxmsg_deferred_prod = dp;
        wakeup(&ctrl_if_rxmsg_deferred_cons);
    }
}

static int ctrl_if_interrupt(void *arg)
{
	control_if_t *ctrl_if = get_ctrl_if();
	int ret = 0;

	if ( ctrl_if_tx_resp_cons != ctrl_if->tx_resp_prod ) {
		ret = 1;
		__ctrl_if_tx_tasklet(0);
	}

	if ( ctrl_if_rx_req_cons != ctrl_if->rx_req_prod ) {
		ret = 1;
		__ctrl_if_rx_tasklet(0);
	}

	return ret;
}

int
ctrl_if_send_message_noblock(
    ctrl_msg_t *msg, 
    ctrl_msg_handler_t hnd,
    unsigned long id)
{
    control_if_t *ctrl_if = get_ctrl_if();
    unsigned long flags;
    int           i;
    int s;

    save_and_cli(flags);
    simple_lock(&ctrl_if_lock);

    while ( TX_FULL(ctrl_if) )
    {
        simple_unlock(&ctrl_if_lock);
	restore_flags(flags);
	s = splhigh();
	if ( ctrl_if_tx_resp_cons != ctrl_if->tx_resp_prod ) {
		__ctrl_if_tx_tasklet(0);
		splx(s);
		save_and_cli(flags);
		simple_lock(&ctrl_if_lock);
	} else {
		splx(s);
        	return EAGAIN;
	}
    }

    msg->id = 0xFF;
    if ( hnd != NULL )
    {
        for ( i = 0; ctrl_if_txmsg_id_mapping[i].fn != NULL; i++ )
            continue;
        ctrl_if_txmsg_id_mapping[i].fn = hnd;
        ctrl_if_txmsg_id_mapping[i].id = id;
        msg->id = i;
    }

    DPRINTK("Tx-Req %u/%u :: %d/%d\n", 
            ctrl_if->tx_req_prod, 
            ctrl_if_tx_resp_cons,
            msg->type, msg->subtype);

    memcpy(&ctrl_if->tx_ring[MASK_CONTROL_IDX(ctrl_if->tx_req_prod)], 
           msg, sizeof(*msg));
    x86_lfence(); /* Write the message before letting the controller peek at it. */
    ctrl_if->tx_req_prod++;

    simple_unlock(&ctrl_if_lock);
    restore_flags(flags);

    ctrl_if_notify_controller();

    return 0;
}

int
ctrl_if_send_message_block(
    ctrl_msg_t *msg, 
    ctrl_msg_handler_t hnd, 
    unsigned long id,
    long wait_state)
{
	int rc;

	while ((rc = ctrl_if_send_message_noblock(msg, hnd, id)) == EAGAIN) {
		/* XXXcl possible race -> add a lock and ltsleep */
#if 1
		HYPERVISOR_yield();
#else
		rc = tsleep((void *) &ctrl_if_tx_wait, PUSER | PCATCH,
		    "ctrl_if", 0);
		if (rc)
			break;
#endif
	}

	return rc;
}

/* Allow a reponse-callback handler to find context of a blocked requester.  */
struct rsp_wait {
    ctrl_msg_t         *msg;  /* Buffer for the response message.            */
    struct task_struct *task; /* The task that is blocked on the response.   */
    int                 done; /* Indicate to 'task' that response is rcv'ed. */
};

static void __ctrl_if_get_response(ctrl_msg_t *msg, unsigned long id)
{
    struct rsp_wait    *wait = (struct rsp_wait *)id;

    memcpy(wait->msg, msg, sizeof(*msg));
    x86_lfence();
    wait->done = 1;

    wakeup(wait);
}

int
ctrl_if_send_message_and_get_response(
    ctrl_msg_t *msg, 
    ctrl_msg_t *rmsg,
    long wait_state)
{
    struct rsp_wait wait;
    int rc;

    wait.msg  = rmsg;
    wait.done = 0;

    if ( (rc = ctrl_if_send_message_block(msg, __ctrl_if_get_response,
                                          (unsigned long)&wait,
                                          wait_state)) != 0 )
        return rc;

    for ( ; ; )
    {
	    if ( wait.done )
		    break;
	    tsleep((void *)&wait, PUSER | PCATCH, "ctrl_if", 0);
    }

    return 0;
}

#ifdef notyet
int
ctrl_if_enqueue_space_callback(
    struct tq_struct *task)
{
    control_if_t *ctrl_if = get_ctrl_if();

    /* Fast path. */
    if ( !TX_FULL(ctrl_if) )
        return 0;

    (void)queue_task(task, &ctrl_if_tx_tq);

    /*
     * We may race execution of the task queue, so return re-checked status. If
     * the task is not executed despite the ring being non-full then we will
     * certainly return 'not full'.
     */
    x86_lfence();
    return TX_FULL(ctrl_if);
}
#endif

void
ctrl_if_send_response(
    ctrl_msg_t *msg)
{
    control_if_t *ctrl_if = get_ctrl_if();
    unsigned long flags;
    ctrl_msg_t   *dmsg;

    /*
     * NB. The response may the original request message, modified in-place.
     * In this situation we may have src==dst, so no copying is required.
     */
    save_and_cli(flags);
    simple_lock(&ctrl_if_lock);

    DPRINTK("Tx-Rsp %u :: %d/%d\n", 
            ctrl_if->rx_resp_prod, 
            msg->type, msg->subtype);

    dmsg = &ctrl_if->rx_ring[MASK_CONTROL_IDX(ctrl_if->rx_resp_prod)];
    if ( dmsg != msg )
        memcpy(dmsg, msg, sizeof(*msg));

    x86_lfence(); /* Write the message before letting the controller peek at it. */
    ctrl_if->rx_resp_prod++;

    simple_unlock(&ctrl_if_lock);
    restore_flags(flags);

    ctrl_if_notify_controller();
}

int
ctrl_if_register_receiver(
    uint8_t type, 
    ctrl_msg_handler_t hnd, 
    unsigned int flags)
{
    unsigned long _flags;
    int inuse;

    save_and_cli(_flags);
    simple_lock(&ctrl_if_lock);

    inuse = (ctrl_if_rxmsg_handler[type] != ctrl_if_rxmsg_default_handler);

    if ( inuse )
    {
        printf("Receiver %p already established for control "
               "messages of type %d.\n", ctrl_if_rxmsg_handler[type], type);
    }
    else
    {
        ctrl_if_rxmsg_handler[type] = hnd;
        xen_atomic_clear_bit((unsigned long *)&ctrl_if_rxmsg_blocking_context, type);
        if ( flags == CALLBACK_IN_BLOCKING_CONTEXT )
        {
            xen_atomic_set_bit((unsigned long *)&ctrl_if_rxmsg_blocking_context, type);
#if 0
            if ( !safe_to_schedule_task )
                BUG();
#endif
        }
    }

    simple_unlock(&ctrl_if_lock);
    restore_flags(_flags);

    return !inuse;
}

void 
ctrl_if_unregister_receiver(
    uint8_t type,
    ctrl_msg_handler_t hnd)
{
    unsigned long flags;

    save_and_cli(flags);
    simple_lock(&ctrl_if_lock);

    if ( ctrl_if_rxmsg_handler[type] != hnd )
        printf("Receiver %p is not registered for control "
               "messages of type %d.\n", hnd, type);
    else
        ctrl_if_rxmsg_handler[type] = ctrl_if_rxmsg_default_handler;

    simple_unlock(&ctrl_if_lock);
    restore_flags(flags);

    /* Ensure that @hnd will not be executed after this function returns. */
    wakeup(&ctrl_if_rxmsg_deferred_cons);
}

#ifdef notyet
void ctrl_if_suspend(void)
{
    free_irq(ctrl_if_irq, NULL);
    unbind_evtchn_from_irq(ctrl_if_evtchn);
}
#endif

void ctrl_if_resume(void)
{
    control_if_t *ctrl_if = get_ctrl_if();

    if (xendomain_is_dom0()) {

        /*
         * The initial domain must create its own domain-controller link.
         * The controller is probably not running at this point, but will
         * pick up its end of the event channel from 
         */
        evtchn_op_t op;
        op.cmd = EVTCHNOP_bind_interdomain;
        op.u.bind_interdomain.dom1 = DOMID_SELF;
        op.u.bind_interdomain.dom2 = DOMID_SELF;
	op.u.bind_interdomain.port1 = 0;
	op.u.bind_interdomain.port2 = 0;
        if ( HYPERVISOR_event_channel_op(&op) != 0 )
		panic("EVTCHNOP_bind_interdomain");
        xen_start_info.domain_controller_evtchn = op.u.bind_interdomain.port1;
        initdom_ctrlif_domcontroller_port   = op.u.bind_interdomain.port2;
    }

    /* Sync up with shared indexes. */
    ctrl_if_tx_resp_cons = ctrl_if->tx_resp_prod;
    ctrl_if_rx_req_cons  = ctrl_if->rx_resp_prod;

    ctrl_if_evtchn = xen_start_info.domain_controller_evtchn;
    aprint_verbose("Domain controller: using event channel %d\n",
	ctrl_if_evtchn);

    event_set_handler(ctrl_if_evtchn, &ctrl_if_interrupt, NULL, IPL_SCHED,
	"ctrlev");
    hypervisor_enable_event(ctrl_if_evtchn);
}

void ctrl_if_early_init(void)
{

	simple_lock_init(&ctrl_if_lock);

	ctrl_if_evtchn = xen_start_info.domain_controller_evtchn;
}

void ctrl_if_init(void)
{
	int i, error;

	for ( i = 0; i < 256; i++ )
		ctrl_if_rxmsg_handler[i] = ctrl_if_rxmsg_default_handler;

	if (ctrl_if_evtchn == -1)
		ctrl_if_early_init();

	error = kthread_create(PRI_NONE, 0, NULL, __ctrl_if_rxmsg_deferred,
	    NULL, NULL, "ctrlif");
	if (error != 0) {
		aprint_error("ctrlif: unable to create kernel thread: "
		    "error %d\n", error);
	}

	ctrl_if_resume();
}

/*
 * !! The following are DANGEROUS FUNCTIONS !!
 * Use with care [for example, see xencons_force_flush()].
 */

int ctrl_if_transmitter_empty(void)
{
    return (get_ctrl_if()->tx_req_prod == ctrl_if_tx_resp_cons);
}

void ctrl_if_discard_responses(void)
{
    ctrl_if_tx_resp_cons = get_ctrl_if()->tx_resp_prod;
}

/*
 * polling functions, for xencons's benefit
 * We need to enable events before calling HYPERVISOR_block().
 * Do this at a high enouth IPL so that no other interrupt will interfere
 * with our polling.
 */

void
ctrl_if_console_poll(void)
{
	int s = splhigh();

	/*
	 * Unmask the ctrl event, and block waiting for an interrupt.
	 * Note that the event we get may not be for the console.
	 * In this case, the console code will call us again.
	 */

	while (ctrl_if_interrupt(NULL) == 0) {
		/* enable interrupts. */
		hypervisor_clear_event(ctrl_if_evtchn);
		hypervisor_unmask_event(ctrl_if_evtchn);
		HYPERVISOR_block();
	}
	splx(s);
}