static NTSTATUS v2v_disconnect_supplicant(const struct v2v_channel *_channel) { NTSTATUS status; struct v2v_channel *channel = (struct v2v_channel *)_channel; v2v_xenops_grant_unmap((void *)channel->cons_sring, channel->u.supplicant.prod_detail); v2v_xenops_grant_unmap(channel->prod_sring, channel->u.supplicant.cons_detail); v2v_xenops_grant_unmap(channel->control, channel->u.supplicant.control_detail); channel->u.supplicant.prod_detail = NULL; channel->u.supplicant.cons_detail = NULL; channel->u.supplicant.control_detail = NULL; channel->prod_sring = NULL; channel->cons_sring = NULL; channel->control = NULL; if (!is_null_EVTCHN_PORT(channel->receive_evtchn_port)) { EvtchnClose(channel->receive_evtchn_port); channel->receive_evtchn_port = null_EVTCHN_PORT(); } if (!is_null_EVTCHN_PORT(channel->send_evtchn_port)) { EvtchnClose(channel->send_evtchn_port); channel->send_evtchn_port = null_EVTCHN_PORT(); } status = v2v_change_local_state(channel, XBT_NIL, v2v_state_disconnected); if (!NT_SUCCESS(status)) return status; v2v_destroy_channel(channel, FALSE); return STATUS_SUCCESS; }
static NTSTATUS V4vInitializeEventChannel(PDEVICE_OBJECT fdo) { XENV4V_EXTENSION *pde = V4vGetDeviceExtension(fdo); KLOCK_QUEUE_HANDLE lqh; KeAcquireInStackQueuedSpinLock(&pde->virqLock, &lqh); if (!is_null_EVTCHN_PORT(pde->virqPort)) { KeReleaseInStackQueuedSpinLock(&lqh); TraceWarning(("V4V VIRQ already bound?\n")); return STATUS_SUCCESS; } pde->virqPort = EvtchnBindVirq(VIRQ_V4V, V4vVirqNotifyIsr, fdo); if (is_null_EVTCHN_PORT(pde->virqPort)) { KeReleaseInStackQueuedSpinLock(&lqh); TraceError(("failed to bind V4V VIRQ\n")); return STATUS_INSUFFICIENT_RESOURCES; } KeReleaseInStackQueuedSpinLock(&lqh); TraceNotice(("V4V VIRQ connected.\n")); return STATUS_SUCCESS; }
static void v2v_destroy_channel(const struct v2v_channel *_chan, BOOLEAN free_temple) { struct v2v_channel *chan = (struct v2v_channel *)_chan; unsigned x; if (chan->remote_state_watch) xenbus_unregister_watch(chan->remote_state_watch); if (chan->local_prefix) ExFreePoolWithTag(chan->local_prefix, V2V_TAG); if (chan->remote_prefix) ExFreePoolWithTag(chan->remote_prefix, V2V_TAG); if (!chan->is_temple) { v2v_xenops_grant_unmap((void *)chan->cons_sring, chan->u.supplicant.prod_detail); v2v_xenops_grant_unmap(chan->prod_sring, chan->u.supplicant.cons_detail); v2v_xenops_grant_unmap(chan->control, chan->u.supplicant.control_detail); } else if (free_temple) { /* and is temple */ if (chan->u.temple.grant_cache) { for (x = 0; x < chan->nr_prod_ring_pages; x++) { if (!is_null_GRANT_REF(chan->u.temple.prod_grefs[x])) GnttabEndForeignAccessCache(chan->u.temple.prod_grefs[x], chan->u.temple.grant_cache); } for (x = 0; x < chan->nr_cons_ring_pages; x++) { if (!is_null_GRANT_REF(chan->u.temple.cons_grefs[x])) GnttabEndForeignAccessCache(chan->u.temple.cons_grefs[x], chan->u.temple.grant_cache); } if (!is_null_GRANT_REF(chan->u.temple.control_gref)) GnttabEndForeignAccessCache(chan->u.temple.control_gref, chan->u.temple.grant_cache); GnttabFreeCache(chan->u.temple.grant_cache); } if (chan->prod_sring) ExFreePoolWithTag(chan->prod_sring, V2V_TAG); if (chan->cons_sring) ExFreePoolWithTag((void*)chan->cons_sring, V2V_TAG); if (chan->control) ExFreePoolWithTag(chan->control, V2V_TAG); } if (!is_null_EVTCHN_PORT(chan->receive_evtchn_port)) EvtchnClose(chan->receive_evtchn_port); if (!is_null_EVTCHN_PORT(chan->send_evtchn_port)) EvtchnClose(chan->send_evtchn_port); EvtchnReleaseDebugCallback(chan->debug_callback); ExFreePoolWithTag(chan, V2V_TAG); }
VOID XenLowerFree( PXEN_LOWER XenLower) { if (!XenLower) { return; } if (XenLower->LateSuspendHandler) { EvtchnUnregisterSuspendHandler(XenLower->LateSuspendHandler); } if (!is_null_EVTCHN_PORT(XenLower->EvtchnPort)) { EvtchnPortStop(XenLower->EvtchnPort); EvtchnClose(XenLower->EvtchnPort); } if (!is_null_GRANT_REF(XenLower->SringGrantRef)) { (VOID)GnttabEndForeignAccess(XenLower->SringGrantRef); } ExFreePool(XenLower); }
static void close_evtchn(struct scsifilt *sf) { if (!is_null_EVTCHN_PORT(sf->evtchn_port)) EvtchnClose(sf->evtchn_port); sf->evtchn_port = null_EVTCHN_PORT(); }
VOID XenLowerScheduleEvtChnDPC( PXEN_LOWER XenLower) { if (!is_null_EVTCHN_PORT(XenLower->EvtchnPort)) { EvtchnRaiseLocally(XenLower->EvtchnPort); } }
NTSTATUS v2v_nc2_prep_message(struct v2v_channel *channel, size_t msg_size, unsigned char type, unsigned char flags, volatile void **payload) { volatile struct netchannel2_msg_hdr *hdr; unsigned short size; unsigned short rounded_size; XM_ASSERT(channel != NULL); XM_ASSERT(payload != NULL); msg_size += sizeof(*hdr); if ( ((msg_size + 7) & ~7) > channel->nc2_rings.producer_payload_bytes ) return STATUS_INVALID_PARAMETER; if (type >= NETCHANNEL2_MSG_PAD) return STATUS_NOT_IMPLEMENTED; size = (unsigned short)msg_size; rounded_size = (size + 7) & ~7; if (channel->nc2_rings.remote_endpoint->consumer_active) v2v_nc2_send_messages(channel); if (!nc2_can_send_payload_bytes(&channel->nc2_rings, rounded_size)) { if (channel->is_sync) KeResetEvent(&channel->s.sync.send_event); if (!nc2_can_send_payload_bytes(&channel->nc2_rings, rounded_size)) return STATUS_RETRY; if (channel->is_sync) KeSetEvent(&channel->s.sync.send_event, IO_NO_INCREMENT, FALSE); } __nc2_avoid_ring_wrap(&channel->nc2_rings, rounded_size); hdr = __nc2_get_message_ptr(&channel->nc2_rings); hdr->size = size; hdr->type = type; hdr->flags = flags; *payload = hdr + 1; channel->nc2_rings.local_prod_pvt += rounded_size; channel->nc2_rings.local_prod_bytes_available -= rounded_size; if (channel->nc2_rings.remote_endpoint->consumer_active && !channel->nc2_rings.local_producer_active && __nc2_flush_would_trigger_event(&channel->nc2_rings)) { channel->nc2_rings.local_endpoint->producer_active = 1; channel->nc2_rings.local_producer_active = 1; XM_ASSERT(!is_null_EVTCHN_PORT(channel->send_evtchn_port)); EvtchnNotifyRemote(channel->send_evtchn_port); } return STATUS_SUCCESS; }
VOID XenLowerDisconnectEvtChnDPC( PXEN_LOWER XenLower) { if (!is_null_EVTCHN_PORT(XenLower->EvtchnPort)) { EvtchnPortStop(XenLower->EvtchnPort); EvtchnClose(XenLower->EvtchnPort); XenLower->EvtchnPort = null_EVTCHN_PORT(); } }
void v2v_nc2_finish_message(struct v2v_channel *channel) { XM_ASSERT(channel != NULL); channel->nc2_rings.local_cons_pvt += (channel->current_message_size + sizeof(struct netchannel2_msg_hdr) + 7) & ~7; if (nc2_finish_messages(&channel->nc2_rings)) { XM_ASSERT(!is_null_EVTCHN_PORT(channel->receive_evtchn_port)); EvtchnNotifyRemote(channel->receive_evtchn_port); } }
static NTSTATUS open_evtchn(struct scsifilt *sf) { DOMAIN_ID domid = sf->backend_domid; close_evtchn(sf); sf->evtchn_port = EvtchnAllocUnbound(domid, handle_evtchn, sf); if (is_null_EVTCHN_PORT(sf->evtchn_port)) return STATUS_INSUFFICIENT_RESOURCES; else return STATUS_SUCCESS; }
NTSTATUS XenLowerEvtChnNotify( PVOID Context) { PXEN_LOWER XenLower = (PXEN_LOWER)Context; if (is_null_EVTCHN_PORT(XenLower->EvtchnPort)) { TraceError((__FUNCTION__ ": no event channel port, cannot notory anything.\n")); return STATUS_UNSUCCESSFUL; } EvtchnNotifyRemote(XenLower->EvtchnPort); return STATUS_SUCCESS; }
BOOLEAN XenLowerConnectEvtChnDPC( PXEN_LOWER XenLower, PEVTCHN_HANDLER_CB DpcCallback, VOID *Context) { XenLower->EvtchnPort = EvtchnAllocUnboundDpc(XenLower->BackendDomid, DpcCallback, Context); if (is_null_EVTCHN_PORT(XenLower->EvtchnPort)) { TraceError((__FUNCTION__ ": failed to allocate DPC for Event Channel.\n")); return FALSE; } return TRUE; }
void v2v_nc2_send_messages(struct v2v_channel *channel) { XM_ASSERT(channel != NULL); if (nc2_flush_ring(&channel->nc2_rings)) { /* The read of consumer_spinning needs to be after the read of * prod_event in nc2_flush_ring(). Both fields are volatile, * so the compiler gives us that for free and we don't need * explicit barriers. */ if (!channel->nc2_rings.remote_endpoint->consumer_spinning) { XM_ASSERT(!is_null_EVTCHN_PORT(channel->send_evtchn_port)); EvtchnNotifyRemote(channel->send_evtchn_port); } if (channel->nc2_rings.local_producer_active) { channel->nc2_rings.local_producer_active = 0; channel->nc2_rings.local_endpoint->producer_active = 0; } } }
static VOID V4vUninitializeEventChannel(PDEVICE_OBJECT fdo) { XENV4V_EXTENSION *pde = V4vGetDeviceExtension(fdo); KLOCK_QUEUE_HANDLE lqh; KeAcquireInStackQueuedSpinLock(&pde->virqLock, &lqh); if (is_null_EVTCHN_PORT(pde->virqPort)) { // This is ok, e.g. getting a stop and remove PnP call KeReleaseInStackQueuedSpinLock(&lqh); return; } EvtchnClose(pde->virqPort); pde->virqPort = null_EVTCHN_PORT(); KeReleaseInStackQueuedSpinLock(&lqh); TraceNotice(("V4V VIRQ disconnected.\n")); }
NTSTATUS v2v_connect(const char *xenbus_prefix, struct v2v_channel **channel, struct v2v_async *async_values) { NTSTATUS status = STATUS_SUCCESS; xenbus_transaction_t xbt = {0}; struct v2v_channel *chan; enum v2v_endpoint_state remote_state; int producer_ring_order; int consumer_ring_order; int x; BOOLEAN xbt_pending = FALSE; XM_ASSERT(channel != NULL); XM_ASSERT(xenbus_prefix != NULL); if (async_values && (!async_values->receive_dpc || !async_values->send_dpc)) return STATUS_INVALID_PARAMETER; *channel = NULL; for (;;) { chan = v2v_make_channel(xenbus_prefix, async_values); if (!chan) return STATUS_NO_MEMORY; xenbus_transaction_start(&xbt); xbt_pending = TRUE; status = v2v_connect_channel_xenbus(chan, xbt); if (!NT_SUCCESS(status)) goto err; status = v2v_get_remote_state_internal(xbt, chan, &remote_state); if (remote_state == v2v_state_unknown) goto err; /* status set to error code */ if (remote_state != v2v_state_listening) { status = STATUS_NO_SUCH_DEVICE; goto err; } status = v2v_xenstore_gather(xbt, chan->remote_prefix, "prod-order", xenstore_gather_type_int, &producer_ring_order, "cons-order", xenstore_gather_type_int, &consumer_ring_order, "control-gref", xenstore_gather_type_alien_grant_ref, &chan->u.supplicant.control_gref, "prod-evtchn", xenstore_gather_type_alien_evtchn_port, &chan->u.supplicant.prod_evtchn_port, "cons-evtchn", xenstore_gather_type_alien_evtchn_port, &chan->u.supplicant.cons_evtchn_port, NULL); if (!NT_SUCCESS(status)) goto err; if (producer_ring_order > MAX_RING_PAGE_ORDER || consumer_ring_order > MAX_RING_PAGE_ORDER) { status = STATUS_INVALID_PARAMETER; goto err; } for (x = 0; x < 1 << producer_ring_order; x++) { status = v2v_read_grantref(chan, xbt, x, TRUE); if (!NT_SUCCESS(status)) goto err; } for (x = 0; x < 1 << consumer_ring_order; x++) { status = v2v_read_grantref(chan, xbt, x, FALSE); if (!NT_SUCCESS(status)) goto err; } status = v2v_xenops_grant_map((volatile void **)&chan->cons_sring, &chan->u.supplicant.prod_detail, chan->peer_domid, 1 << producer_ring_order, chan->u.supplicant.prod_grefs, TRUE); if (!NT_SUCCESS(status)) goto err; status = v2v_xenops_grant_map((volatile void **)&chan->prod_sring, &chan->u.supplicant.cons_detail, chan->peer_domid, 1 << consumer_ring_order, chan->u.supplicant.cons_grefs, FALSE); if (!NT_SUCCESS(status)) goto err; status = v2v_xenops_grant_map((volatile void **)&chan->control, &chan->u.supplicant.control_detail, chan->peer_domid, 1, &chan->u.supplicant.control_gref, FALSE); if (!NT_SUCCESS(status)) goto err; chan->receive_evtchn_port = EvtchnConnectRemotePort(chan->peer_domid, chan->u.supplicant.prod_evtchn_port, (chan->is_sync ? v2v_dpc : async_values->receive_dpc), (chan->is_sync ? &chan->s.sync.receive_event : async_values->receive_ctx)); if (is_null_EVTCHN_PORT(chan->receive_evtchn_port)) { status = STATUS_INSUFFICIENT_RESOURCES; goto err; } chan->send_evtchn_port = EvtchnConnectRemotePort(chan->peer_domid, chan->u.supplicant.cons_evtchn_port, (chan->is_sync ? v2v_dpc : async_values->send_dpc), (chan->is_sync ? &chan->s.sync.send_event : async_values->send_ctx)); if (is_null_EVTCHN_PORT(chan->send_evtchn_port)) { status = STATUS_INSUFFICIENT_RESOURCES; goto err; } status = v2v_change_local_state(chan, xbt, v2v_state_connected); if (!NT_SUCCESS(status)) goto err; status = xenbus_transaction_end(xbt, 0); xbt_pending = FALSE; if (NT_SUCCESS(status)) break; if (status != STATUS_RETRY) goto err; /* cleanup and try again */ v2v_destroy_channel(chan, FALSE); } /* Swap them round: *_ring_order is from the point of view of the temple, but we need the supplicant's viewpoint. */ chan->nr_prod_ring_pages = 1 << consumer_ring_order; chan->nr_cons_ring_pages = 1 << producer_ring_order; v2v_nc2_attach_rings_supplicant(&chan->nc2_rings, chan->cons_sring, PAGE_SIZE << producer_ring_order, chan->prod_sring, PAGE_SIZE << consumer_ring_order, chan->control); *channel = chan; return STATUS_SUCCESS; err: if (xbt_pending) xenbus_transaction_end(xbt, 1); v2v_destroy_channel(chan, FALSE); return status; }
static NTSTATUS XenLowerConnectBackendInternal( PXEN_LOWER XenLower, SUSPEND_TOKEN Token) { NTSTATUS status = STATUS_SUCCESS; XENBUS_STATE state; xenbus_transaction_t xbt; PCHAR fepath; if (is_null_EVTCHN_PORT(XenLower->EvtchnPort)) { TraceError((__FUNCTION__ ": no event channel port, this routine must be called after event channel initialization\n")); return STATUS_UNSUCCESSFUL; } //---------------------------Backend Wait Ready-------------------------------// // // Wait for backend to get ready for initialization. // status = xenbus_change_state(XBT_NIL, XenLower->FrontendPath, "state", XENBUS_STATE_INITIALISING); if (!NT_SUCCESS(status)) { TraceWarning((__FUNCTION__ ": Failed to change front end state to XENBUS_STATE_INITIALISING(%d) status: 0x%x\n", XENBUS_STATE_INITIALISING, status)); // Go on, best effort, chin up } TraceInfo((__FUNCTION__ ": Front end state set to XENBUS_STATE_INITIALISING(%d)\n", XENBUS_STATE_INITIALISING)); state = null_XENBUS_STATE(); for ( ; ; ) { // Turns out suspend tokens are not even used. state = XenbusWaitForBackendStateChange(XenLower->BackendPath, state, NULL, Token); if (same_XENBUS_STATE(state, XENBUS_STATE_INITWAIT)) { break; } if (same_XENBUS_STATE(state, XENBUS_STATE_CLOSING) || is_null_XENBUS_STATE(state)) { TraceError((__FUNCTION__ ": backend '%s' went away before we could connect to it?\n", XenLower->BackendPath)); status = STATUS_UNSUCCESSFUL; break; } } if (status != STATUS_SUCCESS) { return status; } TraceInfo((__FUNCTION__ ": Back end state went to XENBUS_STATE_INITWAIT(%d)\n", XENBUS_STATE_INITWAIT)); //----------------------------Backend Connect---------------------------------// // // Communicate configuration to backend. // fepath = XenLower->FrontendPath; do { xenbus_transaction_start(&xbt); xenbus_write_grant_ref(xbt, fepath, "ring-ref", XenLower->SringGrantRef); xenbus_write_evtchn_port(xbt, fepath, "event-channel", XenLower->EvtchnPort); xenbus_change_state(xbt, fepath, "state", XENBUS_STATE_CONNECTED); status = xenbus_transaction_end(xbt, 0); } while (status == STATUS_RETRY); if (status != STATUS_SUCCESS) { TraceError((__FUNCTION__ ": failed to configure xenstore frontend values.\n")); return STATUS_UNSUCCESSFUL; } TraceInfo((__FUNCTION__ ": Front end state set to XENBUS_STATE_CONNECTED(%d)\n", XENBUS_STATE_CONNECTED)); // // Wait for backend to accept configuration and complete initialization. // state = null_XENBUS_STATE(); for ( ; ; ) { state = XenbusWaitForBackendStateChange(XenLower->BackendPath, state, NULL, Token); if (is_null_XENBUS_STATE(state) || same_XENBUS_STATE(state, XENBUS_STATE_CLOSING) || same_XENBUS_STATE(state, XENBUS_STATE_CLOSED)) { TraceError((__FUNCTION__ ": Failed to connected '%s' <-> '%s' backend state: %d\n", XenLower->FrontendPath, XenLower->BackendPath, state)); status = STATUS_UNSUCCESSFUL; break; } if (same_XENBUS_STATE(state, XENBUS_STATE_CONNECTED)) { TraceNotice((__FUNCTION__ ": Connected '%s' <-> '%s' \n", XenLower->FrontendPath, XenLower->BackendPath)); TraceInfo((__FUNCTION__ ": Back end final state went to XENBUS_STATE_CONNECTED(%d)\n", XENBUS_STATE_CONNECTED)); break; } } return status; }
NTSTATUS v2v_listen(const char *xenbus_prefix, struct v2v_channel **channel, unsigned prod_ring_page_order, unsigned cons_ring_page_order, struct v2v_async *async_values) { NTSTATUS status = STATUS_SUCCESS; unsigned prod_ring_size = PAGE_SIZE << prod_ring_page_order; unsigned cons_ring_size = PAGE_SIZE << cons_ring_page_order; struct v2v_channel *chan; xenbus_transaction_t xbt = {0}; BOOLEAN xbt_pending = FALSE; PHYSICAL_ADDRESS pa; unsigned x; unsigned xen_receive_port, xen_send_port; uint32_t xen_gref; XM_ASSERT(channel != NULL); XM_ASSERT(xenbus_prefix != NULL); if (prod_ring_page_order > MAX_RING_PAGE_ORDER || cons_ring_page_order > MAX_RING_PAGE_ORDER) return STATUS_INVALID_PARAMETER; if (async_values && (!async_values->receive_dpc || !async_values->send_dpc)) return STATUS_INVALID_PARAMETER; *channel = NULL; if (!xenbus_await_initialisation()) return STATUS_NO_SUCH_DEVICE; chan = v2v_make_channel(xenbus_prefix, async_values); if (!chan) return STATUS_NO_MEMORY; chan->is_temple = TRUE; chan->prod_sring = ExAllocatePoolWithTag(NonPagedPool, prod_ring_size, V2V_TAG); chan->cons_sring = ExAllocatePoolWithTag(NonPagedPool, cons_ring_size, V2V_TAG); chan->control = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, V2V_TAG); if (!chan->prod_sring || !chan->cons_sring || !chan->control) goto err_nomem; RtlZeroMemory(chan->prod_sring, prod_ring_size); RtlZeroMemory((void *)chan->cons_sring, cons_ring_size); RtlZeroMemory(chan->control, PAGE_SIZE); chan->nr_prod_ring_pages = 1 << prod_ring_page_order; chan->nr_cons_ring_pages = 1 << cons_ring_page_order; /* pre-allocate the granf refs we are going to need below in a grant cache */ chan->u.temple.grant_cache = GnttabAllocCache(chan->nr_prod_ring_pages + chan->nr_cons_ring_pages + 1); if (!chan->u.temple.grant_cache) goto err_nomem; v2v_nc2_attach_rings_temple(&chan->nc2_rings, chan->cons_sring, cons_ring_size, chan->prod_sring, prod_ring_size, chan->control); for (;;) { xenbus_transaction_start(&xbt); xbt_pending = TRUE; status = v2v_connect_channel_xenbus(chan, xbt); if (!NT_SUCCESS(status)) goto err; for (x = 0; x < 1u << prod_ring_page_order; x++) { pa = MmGetPhysicalAddress((void *)((ULONG_PTR)chan->prod_sring + x * PAGE_SIZE)); chan->u.temple.prod_grefs[x] = GnttabGrantForeignAccessCache(chan->peer_domid, PHYS_TO_PFN(pa), GRANT_MODE_RO, chan->u.temple.grant_cache); XM_ASSERT(!is_null_GRANT_REF(chan->u.temple.prod_grefs[x])); status = v2v_write_grantref(chan, xbt, x, TRUE); if (!NT_SUCCESS(status)) goto err; } for (x = 0; x < 1u << cons_ring_page_order; x++) { pa = MmGetPhysicalAddress((void *)((ULONG_PTR)chan->cons_sring + x * PAGE_SIZE)); chan->u.temple.cons_grefs[x] = GnttabGrantForeignAccessCache(chan->peer_domid, PHYS_TO_PFN(pa), GRANT_MODE_RW, chan->u.temple.grant_cache); XM_ASSERT(!is_null_GRANT_REF(chan->u.temple.cons_grefs[x])); status = v2v_write_grantref(chan, xbt, x, FALSE); if (!NT_SUCCESS(status)) goto err; } pa = MmGetPhysicalAddress((void *)((ULONG_PTR)chan->control)); chan->u.temple.control_gref = GnttabGrantForeignAccessCache(chan->peer_domid, PHYS_TO_PFN(pa), GRANT_MODE_RW, chan->u.temple.grant_cache); XM_ASSERT(!is_null_GRANT_REF(chan->u.temple.control_gref)); chan->receive_evtchn_port = EvtchnAllocUnboundDpc(chan->peer_domid, (chan->is_sync ? v2v_dpc : async_values->receive_dpc), (chan->is_sync ? &chan->s.sync.receive_event : async_values->receive_ctx)); if (is_null_EVTCHN_PORT(chan->receive_evtchn_port)) { status = STATUS_INSUFFICIENT_RESOURCES; goto err; } xen_receive_port = xen_EVTCHN_PORT(chan->receive_evtchn_port); chan->send_evtchn_port = EvtchnAllocUnboundDpc(chan->peer_domid, (chan->is_sync ? v2v_dpc : async_values->send_dpc), (chan->is_sync ? &chan->s.sync.send_event : async_values->send_ctx)); if (is_null_EVTCHN_PORT(chan->send_evtchn_port)) { status = STATUS_INSUFFICIENT_RESOURCES; goto err; } xen_send_port = xen_EVTCHN_PORT(chan->send_evtchn_port); xen_gref = xen_GRANT_REF(chan->u.temple.control_gref); status = v2v_xenstore_scatter(xbt, chan->local_prefix, "prod-order", xenstore_scatter_type_int, prod_ring_page_order, "cons-order", xenstore_scatter_type_int, cons_ring_page_order, "control-gref", xenstore_scatter_type_grant_ref, xen_gref, "prod-evtchn",xenstore_scatter_type_evtchn_port, xen_send_port, "cons-evtchn",xenstore_scatter_type_evtchn_port, xen_receive_port, NULL); if (!NT_SUCCESS(status)) goto err; status = v2v_change_local_state(chan, xbt, v2v_state_listening); if (!NT_SUCCESS(status)) goto err; status = xenbus_transaction_end(xbt, 0); xbt_pending = FALSE; if (NT_SUCCESS(status)) break; if (status != STATUS_RETRY) goto err; /* cleanup for retry */ for (x = 0; x < 1u << prod_ring_page_order; x++) { GnttabEndForeignAccessCache(chan->u.temple.prod_grefs[x], chan->u.temple.grant_cache); } RtlZeroMemory(chan->u.temple.prod_grefs, sizeof(chan->u.temple.prod_grefs)); for (x = 0; x < 1u << cons_ring_page_order; x++) { GnttabEndForeignAccessCache(chan->u.temple.cons_grefs[x], chan->u.temple.grant_cache); } RtlZeroMemory(chan->u.temple.cons_grefs, sizeof(chan->u.temple.cons_grefs)); GnttabEndForeignAccessCache(chan->u.temple.control_gref, chan->u.temple.grant_cache); chan->u.temple.control_gref = null_GRANT_REF(); EvtchnClose(chan->receive_evtchn_port); chan->receive_evtchn_port = null_EVTCHN_PORT(); EvtchnClose(chan->send_evtchn_port); chan->send_evtchn_port = null_EVTCHN_PORT(); xenbus_unregister_watch(chan->remote_state_watch); chan->remote_state_watch = NULL; ExFreePoolWithTag(chan->remote_prefix, V2V_TAG); chan->remote_prefix = NULL; } *channel = chan; return STATUS_SUCCESS; err_nomem: status = STATUS_NO_MEMORY; err: if (xbt_pending) xenbus_transaction_end(xbt, 1); /* since the channel has never been connected here, it is safe to free any temple resources that may have been allocated in this routine */ v2v_destroy_channel(chan, TRUE); return status; }
static NTSTATUS v2v_disconnect_temple(const struct v2v_channel *_channel) { NTSTATUS status = STATUS_SUCCESS; xenbus_transaction_t xbt = {0}; struct v2v_channel *channel = (struct v2v_channel *)_channel; enum v2v_endpoint_state remote_state; BOOLEAN failed, any_failed = FALSE; unsigned x; status = v2v_change_local_state(channel, XBT_NIL, v2v_state_disconnecting); if (!NT_SUCCESS(status)) return status; channel->u.temple.accepted = FALSE; /* Get the other end to disconnect */ for (;;) { xenbus_transaction_start(&xbt); status = v2v_get_remote_state_internal(xbt, channel, &remote_state); switch (remote_state) { case v2v_state_unknown: if (status == STATUS_OBJECT_NAME_NOT_FOUND) break; xenbus_transaction_end(xbt, 1); return status; /* The first two shouldn't really happen, but sometimes can if we've managed to screw (e.g. if two processes try to use the same endpoint). Try to recover. */ case v2v_state_unready: case v2v_state_listening: case v2v_state_disconnecting: case v2v_state_disconnected: case v2v_state_crashed: break; case v2v_state_connected: xenbus_transaction_end(xbt, 1); KeWaitForSingleObject(&channel->control_event, Executive, KernelMode, FALSE, NULL); continue; } status = v2v_change_local_state(channel, xbt, v2v_state_disconnected); if (!NT_SUCCESS(status)) { xenbus_transaction_end(xbt, 1); return status; } status = xenbus_transaction_end(xbt, 0); if (NT_SUCCESS(status)) break; /* drop out of loop and do rest */ if (status == STATUS_RETRY) continue; /* try again */ return status; /* else return the error */ } XM_ASSERT(channel->u.temple.grant_cache != NULL); failed = FALSE; for (x = 0; x < channel->nr_prod_ring_pages; x++) { if (!is_null_GRANT_REF(channel->u.temple.prod_grefs[x])) { status = GnttabEndForeignAccessCache(channel->u.temple.prod_grefs[x], channel->u.temple.grant_cache); if (NT_SUCCESS(status)) channel->u.temple.prod_grefs[x] = null_GRANT_REF(); else failed = any_failed = TRUE; } } if (!failed) { ExFreePoolWithTag(channel->prod_sring, V2V_TAG); channel->prod_sring = NULL; } failed = FALSE; for (x = 0; x < channel->nr_cons_ring_pages; x++) { if (!is_null_GRANT_REF(channel->u.temple.cons_grefs[x])) { status = GnttabEndForeignAccessCache(channel->u.temple.cons_grefs[x], channel->u.temple.grant_cache); if (NT_SUCCESS(status)) channel->u.temple.cons_grefs[x] = null_GRANT_REF(); else failed = any_failed = TRUE; } } if (!failed) { ExFreePoolWithTag((void *)channel->cons_sring, V2V_TAG); channel->cons_sring = NULL; } if (!is_null_GRANT_REF(channel->u.temple.control_gref)) { status = GnttabEndForeignAccessCache(channel->u.temple.control_gref, channel->u.temple.grant_cache); if (NT_SUCCESS(status)) { channel->u.temple.control_gref = null_GRANT_REF(); ExFreePoolWithTag(channel->control, V2V_TAG); channel->control = NULL; } else any_failed = TRUE; } if (!any_failed) GnttabFreeCache(channel->u.temple.grant_cache); if (!is_null_EVTCHN_PORT(channel->receive_evtchn_port)) { EvtchnClose(channel->receive_evtchn_port); channel->receive_evtchn_port = null_EVTCHN_PORT(); } if (!is_null_EVTCHN_PORT(channel->send_evtchn_port)) { EvtchnClose(channel->send_evtchn_port); channel->send_evtchn_port = null_EVTCHN_PORT(); } /* We either freed the rings here or they could not be freed. Prevent v2v_destroy_channel() from trying to free grants/rings with outstanding grant refs */ v2v_destroy_channel(channel, FALSE); return STATUS_SUCCESS; }