/* * * Copyright (c) 2009, Microsoft Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307 USA. * * Authors: * Haiyang Zhang * Hank Janssen * */ #include #include "include/osd.h" #include "include/logging.h" #include "VmbusPrivate.h" /* Internal routines */ static int VmbusChannelCreateGpadlHeader( void * Kbuffer, /* must be phys and virt contiguous */ u32 Size, /* page-size multiple */ VMBUS_CHANNEL_MSGINFO **msgInfo, u32 *MessageCount ); static void DumpVmbusChannel( VMBUS_CHANNEL *Channel ); static void VmbusChannelSetEvent( VMBUS_CHANNEL *Channel ); #if 0 static void DumpMonitorPage( HV_MONITOR_PAGE *MonitorPage ) { int i=0; int j=0; DPRINT_DBG(VMBUS, "monitorPage - %p, trigger state - %d", MonitorPage, MonitorPage->TriggerState); for (i=0; i<4; i++) { DPRINT_DBG(VMBUS, "trigger group (%d) - %llx", i, MonitorPage->TriggerGroup[i].AsUINT64); } for (i=0; i<4; i++) { for (j=0; j<32; j++) { DPRINT_DBG(VMBUS, "latency (%d)(%d) - %llx", i, j, MonitorPage->Latency[i][j]); } } for (i=0; i<4; i++) { for (j=0; j<32; j++) { DPRINT_DBG(VMBUS, "param-conn id (%d)(%d) - %d", i, j, MonitorPage->Parameter[i][j].ConnectionId.Asu32); DPRINT_DBG(VMBUS, "param-flag (%d)(%d) - %d", i, j, MonitorPage->Parameter[i][j].FlagNumber); } } } #endif /*++ Name: VmbusChannelSetEvent() Description: Trigger an event notification on the specified channel. --*/ static void VmbusChannelSetEvent( VMBUS_CHANNEL *Channel ) { HV_MONITOR_PAGE *monitorPage; DPRINT_ENTER(VMBUS); if (Channel->OfferMsg.MonitorAllocated) { /* Each u32 represents 32 channels */ BitSet((u32*)gVmbusConnection.SendInterruptPage + (Channel->OfferMsg.ChildRelId >> 5), Channel->OfferMsg.ChildRelId & 31); monitorPage = (HV_MONITOR_PAGE*)gVmbusConnection.MonitorPages; monitorPage++; /* Get the child to parent monitor page */ BitSet((u32*) &monitorPage->TriggerGroup[Channel->MonitorGroup].Pending, Channel->MonitorBit); } else { VmbusSetEvent(Channel->OfferMsg.ChildRelId); } DPRINT_EXIT(VMBUS); } #if 0 static void VmbusChannelClearEvent( VMBUS_CHANNEL *Channel ) { HV_MONITOR_PAGE *monitorPage; DPRINT_ENTER(VMBUS); if (Channel->OfferMsg.MonitorAllocated) { /* Each u32 represents 32 channels */ BitClear((u32*)gVmbusConnection.SendInterruptPage + (Channel->OfferMsg.ChildRelId >> 5), Channel->OfferMsg.ChildRelId & 31); monitorPage = (HV_MONITOR_PAGE*)gVmbusConnection.MonitorPages; monitorPage++; /* Get the child to parent monitor page */ BitClear((u32*) &monitorPage->TriggerGroup[Channel->MonitorGroup].Pending, Channel->MonitorBit); } DPRINT_EXIT(VMBUS); } #endif /*++; Name: VmbusChannelGetDebugInfo() Description: Retrieve various channel debug info --*/ static void VmbusChannelGetDebugInfo( VMBUS_CHANNEL *Channel, VMBUS_CHANNEL_DEBUG_INFO *DebugInfo ) { HV_MONITOR_PAGE *monitorPage; u8 monitorGroup = (u8)Channel->OfferMsg.MonitorId / 32; u8 monitorOffset = (u8)Channel->OfferMsg.MonitorId % 32; /* u32 monitorBit = 1 << monitorOffset; */ DebugInfo->RelId = Channel->OfferMsg.ChildRelId; DebugInfo->State = Channel->State; memcpy(&DebugInfo->InterfaceType, &Channel->OfferMsg.Offer.InterfaceType, sizeof(GUID)); memcpy(&DebugInfo->InterfaceInstance, &Channel->OfferMsg.Offer.InterfaceInstance, sizeof(GUID)); monitorPage = (HV_MONITOR_PAGE*)gVmbusConnection.MonitorPages; DebugInfo->MonitorId = Channel->OfferMsg.MonitorId; DebugInfo->ServerMonitorPending = monitorPage->TriggerGroup[monitorGroup].Pending; DebugInfo->ServerMonitorLatency = monitorPage->Latency[monitorGroup][ monitorOffset]; DebugInfo->ServerMonitorConnectionId = monitorPage->Parameter[monitorGroup][ monitorOffset].ConnectionId.u.Id; monitorPage++; DebugInfo->ClientMonitorPending = monitorPage->TriggerGroup[monitorGroup].Pending; DebugInfo->ClientMonitorLatency = monitorPage->Latency[monitorGroup][ monitorOffset]; DebugInfo->ClientMonitorConnectionId = monitorPage->Parameter[monitorGroup][ monitorOffset].ConnectionId.u.Id; RingBufferGetDebugInfo(&Channel->Inbound, &DebugInfo->Inbound); RingBufferGetDebugInfo(&Channel->Outbound, &DebugInfo->Outbound); } /*++; Name: VmbusChannelOpen() Description: Open the specified channel. --*/ static int VmbusChannelOpen( VMBUS_CHANNEL *NewChannel, u32 SendRingBufferSize, u32 RecvRingBufferSize, void * UserData, u32 UserDataLen, PFN_CHANNEL_CALLBACK pfnOnChannelCallback, void * Context ) { int ret=0; VMBUS_CHANNEL_OPEN_CHANNEL* openMsg; VMBUS_CHANNEL_MSGINFO* openInfo; void *in, *out; unsigned long flags; DPRINT_ENTER(VMBUS); /* Aligned to page size */ ASSERT(!(SendRingBufferSize & (PAGE_SIZE -1))); ASSERT(!(RecvRingBufferSize & (PAGE_SIZE -1))); NewChannel->OnChannelCallback = pfnOnChannelCallback; NewChannel->ChannelCallbackContext = Context; /* Allocate the ring buffer */ out = PageAlloc((SendRingBufferSize + RecvRingBufferSize) >> PAGE_SHIFT); /* out = kzalloc(sendRingBufferSize + recvRingBufferSize, GFP_KERNEL); */ ASSERT(out); ASSERT(((unsigned long)out & (PAGE_SIZE-1)) == 0); in = (void*)((unsigned long)out + SendRingBufferSize); NewChannel->RingBufferPages = out; NewChannel->RingBufferPageCount = (SendRingBufferSize + RecvRingBufferSize) >> PAGE_SHIFT; RingBufferInit(&NewChannel->Outbound, out, SendRingBufferSize); RingBufferInit(&NewChannel->Inbound, in, RecvRingBufferSize); /* Establish the gpadl for the ring buffer */ DPRINT_DBG(VMBUS, "Establishing ring buffer's gpadl for channel %p...", NewChannel); NewChannel->RingBufferGpadlHandle = 0; ret = VmbusChannelEstablishGpadl(NewChannel, NewChannel->Outbound.RingBuffer, SendRingBufferSize + RecvRingBufferSize, &NewChannel->RingBufferGpadlHandle); DPRINT_DBG(VMBUS, "channel %p ", NewChannel, NewChannel->OfferMsg.ChildRelId, NewChannel->RingBufferGpadlHandle, NewChannel->Outbound.RingBuffer, NewChannel->Outbound.RingSize, NewChannel->Inbound.RingBuffer, NewChannel->Inbound.RingSize, SendRingBufferSize); /* Create and init the channel open message */ openInfo = kmalloc(sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_OPEN_CHANNEL), GFP_KERNEL); ASSERT(openInfo != NULL); openInfo->WaitEvent = WaitEventCreate(); openMsg = (VMBUS_CHANNEL_OPEN_CHANNEL*)openInfo->Msg; openMsg->Header.MessageType = ChannelMessageOpenChannel; openMsg->OpenId = NewChannel->OfferMsg.ChildRelId; /* FIXME */ openMsg->ChildRelId = NewChannel->OfferMsg.ChildRelId; openMsg->RingBufferGpadlHandle = NewChannel->RingBufferGpadlHandle; ASSERT(openMsg->RingBufferGpadlHandle); openMsg->DownstreamRingBufferPageOffset = SendRingBufferSize >> PAGE_SHIFT; openMsg->ServerContextAreaGpadlHandle = 0; /* TODO */ ASSERT(UserDataLen <= MAX_USER_DEFINED_BYTES); if (UserDataLen) { memcpy(openMsg->UserData, UserData, UserDataLen); } spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags); INSERT_TAIL_LIST(&gVmbusConnection.ChannelMsgList, &openInfo->MsgListEntry); spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags); DPRINT_DBG(VMBUS, "Sending channel open msg..."); ret = VmbusPostMessage(openMsg, sizeof(VMBUS_CHANNEL_OPEN_CHANNEL)); if (ret != 0) { DPRINT_ERR(VMBUS, "unable to open channel - %d", ret); goto Cleanup; } /* FIXME: Need to time-out here */ WaitEventWait(openInfo->WaitEvent); if (openInfo->Response.OpenResult.Status == 0) { DPRINT_INFO(VMBUS, "channel <%p> open success!!", NewChannel); } else { DPRINT_INFO(VMBUS, "channel <%p> open failed - %d!!", NewChannel, openInfo->Response.OpenResult.Status); } Cleanup: spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags); REMOVE_ENTRY_LIST(&openInfo->MsgListEntry); spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags); WaitEventClose(openInfo->WaitEvent); kfree(openInfo); DPRINT_EXIT(VMBUS); return 0; } /*++; Name: DumpGpadlBody() Description: Dump the gpadl body message to the console for debugging purposes. --*/ static void DumpGpadlBody( VMBUS_CHANNEL_GPADL_BODY *Gpadl, u32 Len) { int i=0; int pfnCount=0; pfnCount = (Len - sizeof(VMBUS_CHANNEL_GPADL_BODY))/ sizeof(u64); DPRINT_DBG(VMBUS, "gpadl body - len %d pfn count %d", Len, pfnCount); for (i=0; i< pfnCount; i++) { DPRINT_DBG(VMBUS, "gpadl body - %d) pfn %llu", i, Gpadl->Pfn[i]); } } /*++; Name: DumpGpadlHeader() Description: Dump the gpadl header message to the console for debugging purposes. --*/ static void DumpGpadlHeader( VMBUS_CHANNEL_GPADL_HEADER *Gpadl ) { int i=0,j=0; int pageCount=0; DPRINT_DBG(VMBUS, "gpadl header - relid %d, range count %d, range buflen %d", Gpadl->ChildRelId, Gpadl->RangeCount, Gpadl->RangeBufLen); for (i=0; i< Gpadl->RangeCount; i++) { pageCount = Gpadl->Range[i].ByteCount >> PAGE_SHIFT; pageCount = (pageCount > 26)? 26 : pageCount; DPRINT_DBG(VMBUS, "gpadl range %d - len %d offset %d page count %d", i, Gpadl->Range[i].ByteCount, Gpadl->Range[i].ByteOffset, pageCount); for (j=0; j< pageCount; j++) { DPRINT_DBG(VMBUS, "%d) pfn %llu", j, Gpadl->Range[i].PfnArray[j]); } } } /*++; Name: VmbusChannelCreateGpadlHeader() Description: Creates a gpadl for the specified buffer --*/ static int VmbusChannelCreateGpadlHeader( void * Kbuffer, /* from kmalloc() */ u32 Size, /* page-size multiple */ VMBUS_CHANNEL_MSGINFO **MsgInfo, u32 *MessageCount) { int i; int pageCount; unsigned long long pfn; VMBUS_CHANNEL_GPADL_HEADER* gpaHeader; VMBUS_CHANNEL_GPADL_BODY* gpadlBody; VMBUS_CHANNEL_MSGINFO* msgHeader; VMBUS_CHANNEL_MSGINFO* msgBody; u32 msgSize; int pfnSum, pfnCount, pfnLeft, pfnCurr, pfnSize; /* ASSERT( (kbuffer & (PAGE_SIZE-1)) == 0); */ ASSERT( (Size & (PAGE_SIZE-1)) == 0); pageCount = Size >> PAGE_SHIFT; pfn = GetPhysicalAddress(Kbuffer) >> PAGE_SHIFT; /* do we need a gpadl body msg */ pfnSize = MAX_SIZE_CHANNEL_MESSAGE - sizeof(VMBUS_CHANNEL_GPADL_HEADER) - sizeof(GPA_RANGE); pfnCount = pfnSize / sizeof(u64); if (pageCount > pfnCount) /* we need a gpadl body */ { /* fill in the header */ msgSize = sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_GPADL_HEADER) + sizeof(GPA_RANGE) + pfnCount*sizeof(u64); msgHeader = kzalloc(msgSize, GFP_KERNEL); INITIALIZE_LIST_HEAD(&msgHeader->SubMsgList); msgHeader->MessageSize=msgSize; gpaHeader = (VMBUS_CHANNEL_GPADL_HEADER*)msgHeader->Msg; gpaHeader->RangeCount = 1; gpaHeader->RangeBufLen = sizeof(GPA_RANGE) + pageCount*sizeof(u64); gpaHeader->Range[0].ByteOffset = 0; gpaHeader->Range[0].ByteCount = Size; for (i=0; iRange[0].PfnArray[i] = pfn+i; } *MsgInfo = msgHeader; *MessageCount = 1; pfnSum = pfnCount; pfnLeft = pageCount - pfnCount; /* how many pfns can we fit */ pfnSize = MAX_SIZE_CHANNEL_MESSAGE - sizeof(VMBUS_CHANNEL_GPADL_BODY); pfnCount = pfnSize / sizeof(u64); /* fill in the body */ while (pfnLeft) { if (pfnLeft > pfnCount) { pfnCurr = pfnCount; } else { pfnCurr = pfnLeft; } msgSize = sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_GPADL_BODY) + pfnCurr*sizeof(u64); msgBody = kzalloc(msgSize, GFP_KERNEL); ASSERT(msgBody); msgBody->MessageSize = msgSize; (*MessageCount)++; gpadlBody = (VMBUS_CHANNEL_GPADL_BODY*)msgBody->Msg; /* FIXME: Gpadl is u32 and we are using a pointer which could be 64-bit */ /* gpadlBody->Gpadl = kbuffer; */ for (i=0; iPfn[i] = pfn + pfnSum + i; } /* add to msg header */ INSERT_TAIL_LIST(&msgHeader->SubMsgList, &msgBody->MsgListEntry); pfnSum += pfnCurr; pfnLeft -= pfnCurr; } } else { /* everything fits in a header */ msgSize = sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_GPADL_HEADER) + sizeof(GPA_RANGE) + pageCount*sizeof(u64); msgHeader = kzalloc(msgSize, GFP_KERNEL); msgHeader->MessageSize=msgSize; gpaHeader = (VMBUS_CHANNEL_GPADL_HEADER*)msgHeader->Msg; gpaHeader->RangeCount = 1; gpaHeader->RangeBufLen = sizeof(GPA_RANGE) + pageCount*sizeof(u64); gpaHeader->Range[0].ByteOffset = 0; gpaHeader->Range[0].ByteCount = Size; for (i=0; iRange[0].PfnArray[i] = pfn+i; } *MsgInfo = msgHeader; *MessageCount = 1; } return 0; } /*++; Name: VmbusChannelEstablishGpadl() Description: Estabish a GPADL for the specified buffer --*/ static int VmbusChannelEstablishGpadl( VMBUS_CHANNEL *Channel, void * Kbuffer, /* from kmalloc() */ u32 Size, /* page-size multiple */ u32 *GpadlHandle ) { int ret=0; VMBUS_CHANNEL_GPADL_HEADER* gpadlMsg; VMBUS_CHANNEL_GPADL_BODY* gpadlBody; /* VMBUS_CHANNEL_GPADL_CREATED* gpadlCreated; */ VMBUS_CHANNEL_MSGINFO *msgInfo; VMBUS_CHANNEL_MSGINFO *subMsgInfo; u32 msgCount; LIST_ENTRY* anchor; LIST_ENTRY* curr; u32 nextGpadlHandle; unsigned long flags; DPRINT_ENTER(VMBUS); nextGpadlHandle = gVmbusConnection.NextGpadlHandle; InterlockedIncrement((int*)&gVmbusConnection.NextGpadlHandle); VmbusChannelCreateGpadlHeader(Kbuffer, Size, &msgInfo, &msgCount); ASSERT(msgInfo != NULL); ASSERT(msgCount >0); msgInfo->WaitEvent = WaitEventCreate(); gpadlMsg = (VMBUS_CHANNEL_GPADL_HEADER*)msgInfo->Msg; gpadlMsg->Header.MessageType = ChannelMessageGpadlHeader; gpadlMsg->ChildRelId = Channel->OfferMsg.ChildRelId; gpadlMsg->Gpadl = nextGpadlHandle; DumpGpadlHeader(gpadlMsg); spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags); INSERT_TAIL_LIST(&gVmbusConnection.ChannelMsgList, &msgInfo->MsgListEntry); spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags); DPRINT_DBG(VMBUS, "buffer %p, size %d msg cnt %d", Kbuffer, Size, msgCount); DPRINT_DBG(VMBUS, "Sending GPADL Header - len %zd", msgInfo->MessageSize - sizeof(VMBUS_CHANNEL_MSGINFO)); ret = VmbusPostMessage(gpadlMsg, msgInfo->MessageSize - sizeof(VMBUS_CHANNEL_MSGINFO)); if (ret != 0) { DPRINT_ERR(VMBUS, "Unable to open channel - %d", ret); goto Cleanup; } if (msgCount>1) { ITERATE_LIST_ENTRIES(anchor, curr, &msgInfo->SubMsgList) { subMsgInfo = (VMBUS_CHANNEL_MSGINFO*) curr; gpadlBody = (VMBUS_CHANNEL_GPADL_BODY*)subMsgInfo->Msg; gpadlBody->Header.MessageType = ChannelMessageGpadlBody; gpadlBody->Gpadl = nextGpadlHandle; DPRINT_DBG(VMBUS, "Sending GPADL Body - len %zd", subMsgInfo->MessageSize - sizeof(VMBUS_CHANNEL_MSGINFO)); DumpGpadlBody(gpadlBody, subMsgInfo->MessageSize - sizeof(VMBUS_CHANNEL_MSGINFO)); ret = VmbusPostMessage(gpadlBody, subMsgInfo->MessageSize - sizeof(VMBUS_CHANNEL_MSGINFO)); ASSERT(ret == 0); } } WaitEventWait(msgInfo->WaitEvent); /* At this point, we received the gpadl created msg */ DPRINT_DBG(VMBUS, "Received GPADL created (relid %d, status %d handle %x)", Channel->OfferMsg.ChildRelId, msgInfo->Response.GpadlCreated.CreationStatus, gpadlMsg->Gpadl); *GpadlHandle = gpadlMsg->Gpadl; Cleanup: spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags); REMOVE_ENTRY_LIST(&msgInfo->MsgListEntry); spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags); WaitEventClose(msgInfo->WaitEvent); kfree(msgInfo); DPRINT_EXIT(VMBUS); return ret; } /*++; Name: VmbusChannelTeardownGpadl() Description: Teardown the specified GPADL handle --*/ static int VmbusChannelTeardownGpadl( VMBUS_CHANNEL *Channel, u32 GpadlHandle ) { int ret=0; VMBUS_CHANNEL_GPADL_TEARDOWN *msg; VMBUS_CHANNEL_MSGINFO* info; unsigned long flags; DPRINT_ENTER(VMBUS); ASSERT(GpadlHandle != 0); info = kmalloc(sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_GPADL_TEARDOWN), GFP_KERNEL); ASSERT(info != NULL); info->WaitEvent = WaitEventCreate(); msg = (VMBUS_CHANNEL_GPADL_TEARDOWN*)info->Msg; msg->Header.MessageType = ChannelMessageGpadlTeardown; msg->ChildRelId = Channel->OfferMsg.ChildRelId; msg->Gpadl = GpadlHandle; spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags); INSERT_TAIL_LIST(&gVmbusConnection.ChannelMsgList, &info->MsgListEntry); spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags); ret = VmbusPostMessage(msg, sizeof(VMBUS_CHANNEL_GPADL_TEARDOWN)); if (ret != 0) { /* TODO: */ } WaitEventWait(info->WaitEvent); /* Received a torndown response */ spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags); REMOVE_ENTRY_LIST(&info->MsgListEntry); spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags); WaitEventClose(info->WaitEvent); kfree(info); DPRINT_EXIT(VMBUS); return ret; } /*++ Name: VmbusChannelClose() Description: Close the specified channel --*/ static void VmbusChannelClose( VMBUS_CHANNEL *Channel ) { int ret=0; VMBUS_CHANNEL_CLOSE_CHANNEL* msg; VMBUS_CHANNEL_MSGINFO* info; unsigned long flags; DPRINT_ENTER(VMBUS); /* Stop callback and cancel the timer asap */ Channel->OnChannelCallback = NULL; TimerStop(Channel->PollTimer); /* Send a closing message */ info = kmalloc(sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_CLOSE_CHANNEL), GFP_KERNEL); ASSERT(info != NULL); /* info->waitEvent = WaitEventCreate(); */ msg = (VMBUS_CHANNEL_CLOSE_CHANNEL*)info->Msg; msg->Header.MessageType = ChannelMessageCloseChannel; msg->ChildRelId = Channel->OfferMsg.ChildRelId; ret = VmbusPostMessage(msg, sizeof(VMBUS_CHANNEL_CLOSE_CHANNEL)); if (ret != 0) { /* TODO: */ } /* Tear down the gpadl for the channel's ring buffer */ if (Channel->RingBufferGpadlHandle) { VmbusChannelTeardownGpadl(Channel, Channel->RingBufferGpadlHandle); } /* TODO: Send a msg to release the childRelId */ /* Cleanup the ring buffers for this channel */ RingBufferCleanup(&Channel->Outbound); RingBufferCleanup(&Channel->Inbound); PageFree(Channel->RingBufferPages, Channel->RingBufferPageCount); kfree(info); /* * If we are closing the channel during an error path in * opening the channel, don't free the channel since the * caller will free the channel */ if (Channel->State == CHANNEL_OPEN_STATE) { spin_lock_irqsave(&gVmbusConnection.channel_lock, flags); REMOVE_ENTRY_LIST(&Channel->ListEntry); spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags); FreeVmbusChannel(Channel); } DPRINT_EXIT(VMBUS); } /*++ Name: VmbusChannelSendPacket() Description: Send the specified buffer on the given channel --*/ static int VmbusChannelSendPacket( VMBUS_CHANNEL *Channel, const void * Buffer, u32 BufferLen, u64 RequestId, VMBUS_PACKET_TYPE Type, u32 Flags ) { int ret=0; VMPACKET_DESCRIPTOR desc; u32 packetLen = sizeof(VMPACKET_DESCRIPTOR) + BufferLen; u32 packetLenAligned = ALIGN_UP(packetLen, sizeof(u64)); SG_BUFFER_LIST bufferList[3]; u64 alignedData=0; DPRINT_ENTER(VMBUS); DPRINT_DBG(VMBUS, "channel %p buffer %p len %d", Channel, Buffer, BufferLen); DumpVmbusChannel(Channel); ASSERT((packetLenAligned - packetLen) < sizeof(u64)); /* Setup the descriptor */ desc.Type = Type; /* VmbusPacketTypeDataInBand; */ desc.Flags = Flags; /* VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; */ desc.DataOffset8 = sizeof(VMPACKET_DESCRIPTOR) >> 3; /* in 8-bytes granularity */ desc.Length8 = (u16)(packetLenAligned >> 3); desc.TransactionId = RequestId; bufferList[0].Data = &desc; bufferList[0].Length = sizeof(VMPACKET_DESCRIPTOR); bufferList[1].Data = Buffer; bufferList[1].Length = BufferLen; bufferList[2].Data = &alignedData; bufferList[2].Length = packetLenAligned - packetLen; ret = RingBufferWrite( &Channel->Outbound, bufferList, 3); /* TODO: We should determine if this is optional */ if (ret == 0 && !GetRingBufferInterruptMask(&Channel->Outbound)) { VmbusChannelSetEvent(Channel); } DPRINT_EXIT(VMBUS); return ret; } /*++ Name: VmbusChannelSendPacketPageBuffer() Description: Send a range of single-page buffer packets using a GPADL Direct packet type. --*/ static int VmbusChannelSendPacketPageBuffer( VMBUS_CHANNEL *Channel, PAGE_BUFFER PageBuffers[], u32 PageCount, void * Buffer, u32 BufferLen, u64 RequestId ) { int ret=0; int i=0; struct VMBUS_CHANNEL_PACKET_PAGE_BUFFER desc; u32 descSize; u32 packetLen; u32 packetLenAligned; SG_BUFFER_LIST bufferList[3]; u64 alignedData=0; DPRINT_ENTER(VMBUS); ASSERT(PageCount <= MAX_PAGE_BUFFER_COUNT); DumpVmbusChannel(Channel); /* Adjust the size down since VMBUS_CHANNEL_PACKET_PAGE_BUFFER is the largest size we support */ descSize = sizeof(struct VMBUS_CHANNEL_PACKET_PAGE_BUFFER) - ((MAX_PAGE_BUFFER_COUNT - PageCount)*sizeof(PAGE_BUFFER)); packetLen = descSize + BufferLen; packetLenAligned = ALIGN_UP(packetLen, sizeof(u64)); ASSERT((packetLenAligned - packetLen) < sizeof(u64)); /* Setup the descriptor */ desc.Type = VmbusPacketTypeDataUsingGpaDirect; desc.Flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; desc.DataOffset8 = descSize >> 3; /* in 8-bytes grandularity */ desc.Length8 = (u16)(packetLenAligned >> 3); desc.TransactionId = RequestId; desc.RangeCount = PageCount; for (i=0; iOutbound, bufferList, 3); /* TODO: We should determine if this is optional */ if (ret == 0 && !GetRingBufferInterruptMask(&Channel->Outbound)) { VmbusChannelSetEvent(Channel); } DPRINT_EXIT(VMBUS); return ret; } /*++ Name: VmbusChannelSendPacketMultiPageBuffer() Description: Send a multi-page buffer packet using a GPADL Direct packet type. --*/ static int VmbusChannelSendPacketMultiPageBuffer( VMBUS_CHANNEL *Channel, MULTIPAGE_BUFFER *MultiPageBuffer, void * Buffer, u32 BufferLen, u64 RequestId ) { int ret=0; struct VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER desc; u32 descSize; u32 packetLen; u32 packetLenAligned; SG_BUFFER_LIST bufferList[3]; u64 alignedData=0; u32 PfnCount = NUM_PAGES_SPANNED(MultiPageBuffer->Offset, MultiPageBuffer->Length); DPRINT_ENTER(VMBUS); DumpVmbusChannel(Channel); DPRINT_DBG(VMBUS, "data buffer - offset %u len %u pfn count %u", MultiPageBuffer->Offset, MultiPageBuffer->Length, PfnCount); ASSERT(PfnCount > 0); ASSERT(PfnCount <= MAX_MULTIPAGE_BUFFER_COUNT); /* Adjust the size down since VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER is the largest size we support */ descSize = sizeof(struct VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER) - ((MAX_MULTIPAGE_BUFFER_COUNT - PfnCount)*sizeof(u64)); packetLen = descSize + BufferLen; packetLenAligned = ALIGN_UP(packetLen, sizeof(u64)); ASSERT((packetLenAligned - packetLen) < sizeof(u64)); /* Setup the descriptor */ desc.Type = VmbusPacketTypeDataUsingGpaDirect; desc.Flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; desc.DataOffset8 = descSize >> 3; /* in 8-bytes grandularity */ desc.Length8 = (u16)(packetLenAligned >> 3); desc.TransactionId = RequestId; desc.RangeCount = 1; desc.Range.Length = MultiPageBuffer->Length; desc.Range.Offset = MultiPageBuffer->Offset; memcpy(desc.Range.PfnArray, MultiPageBuffer->PfnArray, PfnCount*sizeof(u64)); bufferList[0].Data = &desc; bufferList[0].Length = descSize; bufferList[1].Data = Buffer; bufferList[1].Length = BufferLen; bufferList[2].Data = &alignedData; bufferList[2].Length = packetLenAligned - packetLen; ret = RingBufferWrite( &Channel->Outbound, bufferList, 3); /* TODO: We should determine if this is optional */ if (ret == 0 && !GetRingBufferInterruptMask(&Channel->Outbound)) { VmbusChannelSetEvent(Channel); } DPRINT_EXIT(VMBUS); return ret; } /*++ Name: VmbusChannelRecvPacket() Description: Retrieve the user packet on the specified channel --*/ /* TODO: Do we ever receive a gpa direct packet other than the ones we send ? */ static int VmbusChannelRecvPacket( VMBUS_CHANNEL *Channel, void * Buffer, u32 BufferLen, u32* BufferActualLen, u64* RequestId ) { VMPACKET_DESCRIPTOR desc; u32 packetLen; u32 userLen; int ret; unsigned long flags; DPRINT_ENTER(VMBUS); *BufferActualLen = 0; *RequestId = 0; spin_lock_irqsave(&Channel->inbound_lock, flags); ret = RingBufferPeek(&Channel->Inbound, &desc, sizeof(VMPACKET_DESCRIPTOR)); if (ret != 0) { spin_unlock_irqrestore(&Channel->inbound_lock, flags); /* DPRINT_DBG(VMBUS, "nothing to read!!"); */ DPRINT_EXIT(VMBUS); return 0; } /* VmbusChannelClearEvent(Channel); */ packetLen = desc.Length8 << 3; userLen = packetLen - (desc.DataOffset8 << 3); /* ASSERT(userLen > 0); */ DPRINT_DBG(VMBUS, "packet received on channel %p relid %d ", Channel, Channel->OfferMsg.ChildRelId, desc.Type, desc.Flags, desc.TransactionId, packetLen, userLen); *BufferActualLen = userLen; if (userLen > BufferLen) { spin_unlock_irqrestore(&Channel->inbound_lock, flags); DPRINT_ERR(VMBUS, "buffer too small - got %d needs %d", BufferLen, userLen); DPRINT_EXIT(VMBUS); return -1; } *RequestId = desc.TransactionId; /* Copy over the packet to the user buffer */ ret = RingBufferRead(&Channel->Inbound, Buffer, userLen, (desc.DataOffset8 << 3)); spin_unlock_irqrestore(&Channel->inbound_lock, flags); DPRINT_EXIT(VMBUS); return 0; } /*++ Name: VmbusChannelRecvPacketRaw() Description: Retrieve the raw packet on the specified channel --*/ static int VmbusChannelRecvPacketRaw( VMBUS_CHANNEL *Channel, void * Buffer, u32 BufferLen, u32* BufferActualLen, u64* RequestId ) { VMPACKET_DESCRIPTOR desc; u32 packetLen; u32 userLen; int ret; unsigned long flags; DPRINT_ENTER(VMBUS); *BufferActualLen = 0; *RequestId = 0; spin_lock_irqsave(&Channel->inbound_lock, flags); ret = RingBufferPeek(&Channel->Inbound, &desc, sizeof(VMPACKET_DESCRIPTOR)); if (ret != 0) { spin_unlock_irqrestore(&Channel->inbound_lock, flags); /* DPRINT_DBG(VMBUS, "nothing to read!!"); */ DPRINT_EXIT(VMBUS); return 0; } /* VmbusChannelClearEvent(Channel); */ packetLen = desc.Length8 << 3; userLen = packetLen - (desc.DataOffset8 << 3); DPRINT_DBG(VMBUS, "packet received on channel %p relid %d ", Channel, Channel->OfferMsg.ChildRelId, desc.Type, desc.Flags, desc.TransactionId, packetLen, userLen); *BufferActualLen = packetLen; if (packetLen > BufferLen) { spin_unlock_irqrestore(&Channel->inbound_lock, flags); DPRINT_ERR(VMBUS, "buffer too small - needed %d bytes but got space for only %d bytes", packetLen, BufferLen); DPRINT_EXIT(VMBUS); return -2; } *RequestId = desc.TransactionId; /* Copy over the entire packet to the user buffer */ ret = RingBufferRead(&Channel->Inbound, Buffer, packetLen, 0); spin_unlock_irqrestore(&Channel->inbound_lock, flags); DPRINT_EXIT(VMBUS); return 0; } /*++ Name: VmbusChannelOnChannelEvent() Description: Channel event callback --*/ static void VmbusChannelOnChannelEvent( VMBUS_CHANNEL *Channel ) { DumpVmbusChannel(Channel); ASSERT(Channel->OnChannelCallback); #ifdef ENABLE_POLLING TimerStop(Channel->PollTimer); Channel->OnChannelCallback(Channel->ChannelCallbackContext); TimerStart(Channel->PollTimer, 100 /* 100us */); #else Channel->OnChannelCallback(Channel->ChannelCallbackContext); #endif } /*++ Name: VmbusChannelOnTimer() Description: Timer event callback --*/ static void VmbusChannelOnTimer( void *Context ) { VMBUS_CHANNEL *channel = (VMBUS_CHANNEL*)Context; if (channel->OnChannelCallback) { channel->OnChannelCallback(channel->ChannelCallbackContext); #ifdef ENABLE_POLLING TimerStart(channel->PollTimer, 100 /* 100us */); #endif } } /*++ Name: DumpVmbusChannel() Description: Dump vmbus channel info to the console --*/ static void DumpVmbusChannel( VMBUS_CHANNEL *Channel ) { DPRINT_DBG(VMBUS, "Channel (%d)", Channel->OfferMsg.ChildRelId); DumpRingInfo(&Channel->Outbound, "Outbound "); DumpRingInfo(&Channel->Inbound, "Inbound "); } /* eof */