/****************************************************************************** * ctrl_if.c * * Management functions for special interface to the domain controller. * * Copyright (c) 2004, K A Fraser */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "xen.h" #define MSGOFFSET(s, m) (ulong)((((s*)0)->m)) #define LOG(a) #define SETUPLOG(a) extern start_info_t xen_start_info; /* * 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; static int ctrl_if_irq; static Lock 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]; /* we have three procs, two for the kproc and one for any client * of a kproc */ Rendez kproc_rx_r, kproc_rx_defer_r; Lock kproc_rx_defer_l; #define get_ctrl_if() ((control_if_t *)((char *)HYPERVISOR_shared_info + 2048)) #define TX_FULL(_c) \ (((_c)->tx_req_prod - ctrl_if_tx_resp_cons) == CONTROL_RING_SIZE) void ctrl_if_interrupt(Ureg *, void *) { static void ctrl_rx_flush(void); //LOG(dp("ctrl_if_interrupt\n")); //wakeup(&kproc_rx_r); ctrl_rx_flush(); } static void ctrl_if_notify_controller(void) { notify_via_evtchn(ctrl_if_evtchn); } static void ctrl_if_rxmsg_default_handler(ctrl_msg_t *msg, unsigned long ) { msg->length = 0; ctrl_if_send_response(msg); } /* we expect you locked the lock ... */ /* we should do this in a lockless way, using atomic inc, but that's for * later if we even need it */ static void ctrl_tx_flush(void) { control_if_t *ctrl_if = get_ctrl_if(); ctrl_msg_t *msg; LOG(dp("TX: start ctrl_if %p\n", ctrl_if)); while ( ctrl_if_tx_resp_cons != ctrl_if->tx_resp_prod ) { LOG(dp("TX:cons 0x%ulx prod 0x%ulx\n", ctrl_if_tx_resp_cons, ctrl_if->tx_resp_prod)); //break; msg = &ctrl_if->tx_ring[MASK_CONTROL_IDX(ctrl_if_tx_resp_cons)]; /* 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); //smp_mb(); /* Execute, /then/ free. */ ctrl_if_txmsg_id_mapping[msg->id].fn = 0; } /* * 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. */ ctrl_if_tx_resp_cons++; } } int kproc_rx_defer_kick(void *) { return ctrl_if_rxmsg_deferred_cons != ctrl_if_rxmsg_deferred_prod; } static void kproc_rx_defer(void *) { ctrl_msg_t *msg; LOG(dp("RXDEFER: start\n")); while(1) { while ( ctrl_if_rxmsg_deferred_cons != ctrl_if_rxmsg_deferred_prod ) { control_msg_t copymsg; LOG(dp("krpoc_rx_defer: something but break out\n")); ilock(&kproc_rx_defer_l); msg = &ctrl_if_rxmsg_deferred[MASK_CONTROL_IDX( ctrl_if_rxmsg_deferred_cons++)]; /* dq and copy but leave lock held until done for now */ copymsg = *msg; iunlock(&kproc_rx_defer_l); (*ctrl_if_rxmsg_handler[copymsg.type])(©msg, 0); // iunlock(&kproc_rx_defer_l); } LOG(dp("RXDEFER: sleep\n")); sleep(&kproc_rx_defer_r, kproc_rx_defer_kick, 0); LOG(dp("RXDEFER: woken up\n")); } } int kproc_rx_kick(void *) { control_if_t *ctrl_if = get_ctrl_if(); return (ctrl_if_rx_req_cons != ctrl_if->rx_req_prod); } static void ctrl_rx_flush(void *) { control_if_t *ctrl_if = get_ctrl_if(); ctrl_msg_t msg, *pmsg; /* LOG(dp("RX: start\n")); LOG(dp("ctrl_if is %p\n", ctrl_if)); while(1) { */ while ( ctrl_if_rx_req_cons != ctrl_if->rx_req_prod ) { LOG(dp("kproc_rx: something cons is 0x%ulx, proc 0x%ulx\n", ctrl_if_rx_req_cons, ctrl_if->rx_req_prod);) // break; pmsg = &ctrl_if->rx_ring[MASK_CONTROL_IDX(ctrl_if_rx_req_cons++)]; memmove(&msg, pmsg, MSGOFFSET(ctrl_msg_t, msg)); if ( msg.length != 0 ) memmove(msg.msg, pmsg->msg, msg.length); LOG(dp("kprox_rx :msg.lenght is %d, type %d\n", msg.length, msg.type)); if ( synch_test_bit(msg.type, ctrl_if_rxmsg_blocking_context) ) { LOG(dp("RX: It's blocking!\n")); ilock(&kproc_rx_defer_l); pmsg = &ctrl_if_rxmsg_deferred[MASK_CONTROL_IDX( ctrl_if_rxmsg_deferred_prod++)]; memmove(pmsg, &msg, MSGOFFSET(ctrl_msg_t, msg) + msg.length); iunlock(&kproc_rx_defer_l); wakeup(&kproc_rx_defer_r); // schedule_task(&ctrl_if_rxmsg_deferred_tq); } else { LOG(dp("RX: not blocking\n")); (*ctrl_if_rxmsg_handler[msg.type])(&msg, 0); } } /* LOG(dp("RX: sleep\n")); sleep(&kproc_rx_r, kproc_rx_kick, 0); LOG(dp("RX: woken up\n")); } */ } static int client_tx_kick(void *) { control_if_t *ctrl_if = get_ctrl_if(); return (!TX_FULL(ctrl_if)); } 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(); int i; ilock(&ctrl_if_lock); if ( TX_FULL(ctrl_if) ) { iunlock(&ctrl_if_lock); LOG(dp("nb:TXF\n")); return -1; } msg->id = 0xFF; if ( hnd ) { for ( i = 0; ctrl_if_txmsg_id_mapping[i].fn; i++ ) continue; ctrl_if_txmsg_id_mapping[i].fn = hnd; ctrl_if_txmsg_id_mapping[i].id = id; msg->id = i; } memmove(&ctrl_if->tx_ring[MASK_CONTROL_IDX(ctrl_if->tx_req_prod)], msg, sizeof(*msg)); //wmb(); /* Write the message before letting the controller peek at it. */ ctrl_if->tx_req_prod++; iunlock(&ctrl_if_lock); LOG(dp("_noblock: call notify_controller\n")); ctrl_if_notify_controller(); LOG(dp("_noblock: done\n")); 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; // LOG(dp("_block: start\n")); /* Fast path. */ if ( (rc = ctrl_if_send_message_noblock(msg, hnd, id)) != -1 ){ LOG(dp("ctrl_if_send_message_block: rc %d\n", rc)); return rc; } for ( ; ; ) { ilock(&ctrl_if_lock); ctrl_tx_flush(); iunlock(&ctrl_if_lock); /* this will return if there is space in the q */ if ( (rc = ctrl_if_send_message_noblock(msg, hnd, id)) != -1 ) break; /* let the hypervisor have its way with us for a while */ HYPERVISOR_yield(); /* in theory, at this point, there really ought to be some room * in that damned queue */ } LOG(dp("ctrl_if_send_message_block: rc %d\n", rc)); return rc; } #ifdef NOT 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'. */ // smp_mb(); return TX_FULL(ctrl_if); } #endif void ctrl_if_send_response(ctrl_msg_t *msg) { control_if_t *ctrl_if = get_ctrl_if(); 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. */ ilock(&ctrl_if_lock); dmsg = &ctrl_if->rx_ring[MASK_CONTROL_IDX(ctrl_if->rx_resp_prod)]; if ( dmsg != msg ) memmove(dmsg, msg, sizeof(*msg)); /// wmb(); /* Write the message before letting the controller peek at it. */ ctrl_if->rx_resp_prod++; iunlock(&ctrl_if_lock); ctrl_if_notify_controller(); } int ctrl_if_register_receiver( u8 type, ctrl_msg_handler_t hnd, unsigned int flags) { int inuse; ilock(&ctrl_if_lock); inuse = (ctrl_if_rxmsg_handler[type] != ctrl_if_rxmsg_default_handler); if ( inuse ) { dp("Receiver %p already established for control " "messages of type %d.\n", ctrl_if_rxmsg_handler[type], type); } else { ctrl_if_rxmsg_handler[type] = hnd; synch_clear_bit(type, ctrl_if_rxmsg_blocking_context); if ( flags == CALLBACK_IN_BLOCKING_CONTEXT ) { synch_set_bit(type, ctrl_if_rxmsg_blocking_context); // if ( !safe_to_schedule_task ) // BUG(); } } iunlock(&ctrl_if_lock); return !inuse; } void ctrl_if_unregister_receiver(u8 type, ctrl_msg_handler_t hnd) { ilock(&ctrl_if_lock); if ( ctrl_if_rxmsg_handler[type] != hnd ) dp("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; iunlock(&ctrl_if_lock); /* Ensure that @hnd will not be executed after this function returns. */ //tasklet_unlock_wait(&ctrl_if_rx_tasklet); } #ifdef NOTNOW void ctrl_if_suspend(void) { free_irq(ctrl_if_irq, 0); unbind_evtchn_from_irq(ctrl_if_evtchn); } #endif void ctrl_if_resume(void) { // extern int bind_evtchn_to_irq(int evtchn); extended_start_info_t *esi = (extended_start_info_t *)&xen_start_info; extern void xencons_rx_msg(ctrl_msg_t *msg, unsigned long l); ctrl_if_tx_resp_cons = 0; ctrl_if_rx_req_cons = 0; ctrl_if_evtchn = esi->domain_controller_evtchn; dp("evtchn from start info is %d but that's bullshit ...\n", ctrl_if_evtchn); ctrl_if_irq = bind_evtchn_to_irq(ctrl_if_evtchn, 0); dp("evtchn is %d, ctrl_if_irq is %d\n", ctrl_if_evtchn, ctrl_if_irq); intrenable(ctrl_if_irq, ctrl_if_interrupt, nil, 0, "Control if interrupt"); SETUPLOG(dp("ctrl if enabled\n")); ctrl_if_register_receiver(CMSG_CONSOLE, xencons_rx_msg, 0); } void ctrl_if_init(void) { int i; for ( i = 0; i < 256; i++ ) ctrl_if_rxmsg_handler[i] = ctrl_if_rxmsg_default_handler; //ctrl_if_rxmsg_deferred_tq.routine = __ctrl_if_rxmsg_deferred; //spin_lock_init(&ctrl_if_lock); // ctrl_if_resume(); } void ctrl_if_kproc(void) { print(" STARTING kprocs\n"); // kproc("#|kproc_rx", kproc_rx, 0); kproc("#|kproc_rx_defer", kproc_rx_defer, 0); LOG(dp("Done starting kprocs\n")); } /* * !! 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; }