| /* |
| * 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 <haiyangz@microsoft.com> |
| * Hank Janssen <hjanssen@microsoft.com> |
| */ |
| #include <linux/kernel.h> |
| #include <linux/mm.h> |
| #include <linux/slab.h> |
| #include <linux/module.h> |
| #include "osd.h" |
| #include "logging.h" |
| #include "vmbus_private.h" |
| |
| /* Internal routines */ |
| static int VmbusChannelCreateGpadlHeader( |
| void *Kbuffer, /* must be phys and virt contiguous */ |
| u32 Size, /* page-size multiple */ |
| struct vmbus_channel_msginfo **msgInfo, |
| u32 *MessageCount); |
| static void DumpVmbusChannel(struct vmbus_channel *channel); |
| static void VmbusChannelSetEvent(struct vmbus_channel *channel); |
| |
| |
| #if 0 |
| static void DumpMonitorPage(struct 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 |
| |
| /* |
| * VmbusChannelSetEvent - Trigger an event notification on the specified |
| * channel. |
| */ |
| static void VmbusChannelSetEvent(struct vmbus_channel *Channel) |
| { |
| struct hv_monitor_page *monitorPage; |
| |
| DPRINT_ENTER(VMBUS); |
| |
| if (Channel->OfferMsg.MonitorAllocated) { |
| /* Each u32 represents 32 channels */ |
| set_bit(Channel->OfferMsg.ChildRelId & 31, |
| (unsigned long *) gVmbusConnection.SendInterruptPage + |
| (Channel->OfferMsg.ChildRelId >> 5)); |
| |
| monitorPage = gVmbusConnection.MonitorPages; |
| monitorPage++; /* Get the child to parent monitor page */ |
| |
| set_bit(Channel->MonitorBit, |
| (unsigned long *)&monitorPage->TriggerGroup |
| [Channel->MonitorGroup].Pending); |
| |
| } else { |
| VmbusSetEvent(Channel->OfferMsg.ChildRelId); |
| } |
| |
| DPRINT_EXIT(VMBUS); |
| } |
| |
| #if 0 |
| static void VmbusChannelClearEvent(struct vmbus_channel *channel) |
| { |
| struct hv_monitor_page *monitorPage; |
| |
| DPRINT_ENTER(VMBUS); |
| |
| if (Channel->OfferMsg.MonitorAllocated) { |
| /* Each u32 represents 32 channels */ |
| clear_bit(Channel->OfferMsg.ChildRelId & 31, |
| (unsigned long *)gVmbusConnection.SendInterruptPage + |
| (Channel->OfferMsg.ChildRelId >> 5)); |
| |
| monitorPage = |
| (struct hv_monitor_page *)gVmbusConnection.MonitorPages; |
| monitorPage++; /* Get the child to parent monitor page */ |
| |
| clear_bit(Channel->MonitorBit, |
| (unsigned long *)&monitorPage->TriggerGroup |
| [Channel->MonitorGroup].Pending); |
| } |
| |
| DPRINT_EXIT(VMBUS); |
| } |
| |
| #endif |
| /* |
| * VmbusChannelGetDebugInfo -Retrieve various channel debug info |
| */ |
| void VmbusChannelGetDebugInfo(struct vmbus_channel *Channel, |
| struct vmbus_channel_debug_info *DebugInfo) |
| { |
| struct 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(struct hv_guid)); |
| memcpy(&DebugInfo->InterfaceInstance, |
| &Channel->OfferMsg.Offer.InterfaceInstance, |
| sizeof(struct hv_guid)); |
| |
| monitorPage = (struct 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); |
| } |
| |
| /* |
| * VmbusChannelOpen - Open the specified channel. |
| */ |
| int VmbusChannelOpen(struct vmbus_channel *NewChannel, u32 SendRingBufferSize, |
| u32 RecvRingBufferSize, void *UserData, u32 UserDataLen, |
| void (*OnChannelCallback)(void *context), void *Context) |
| { |
| struct vmbus_channel_open_channel *openMsg; |
| struct vmbus_channel_msginfo *openInfo = NULL; |
| void *in, *out; |
| unsigned long flags; |
| int ret, err = 0; |
| |
| DPRINT_ENTER(VMBUS); |
| |
| /* Aligned to page size */ |
| /* ASSERT(!(SendRingBufferSize & (PAGE_SIZE - 1))); */ |
| /* ASSERT(!(RecvRingBufferSize & (PAGE_SIZE - 1))); */ |
| |
| NewChannel->OnChannelCallback = OnChannelCallback; |
| NewChannel->ChannelCallbackContext = Context; |
| |
| /* Allocate the ring buffer */ |
| out = osd_PageAlloc((SendRingBufferSize + RecvRingBufferSize) |
| >> PAGE_SHIFT); |
| if (!out) |
| return -ENOMEM; |
| |
| /* ASSERT(((unsigned long)out & (PAGE_SIZE-1)) == 0); */ |
| |
| in = (void *)((unsigned long)out + SendRingBufferSize); |
| |
| NewChannel->RingBufferPages = out; |
| NewChannel->RingBufferPageCount = (SendRingBufferSize + |
| RecvRingBufferSize) >> PAGE_SHIFT; |
| |
| ret = RingBufferInit(&NewChannel->Outbound, out, SendRingBufferSize); |
| if (ret != 0) { |
| err = ret; |
| goto errorout; |
| } |
| |
| ret = RingBufferInit(&NewChannel->Inbound, in, RecvRingBufferSize); |
| if (ret != 0) { |
| err = ret; |
| goto errorout; |
| } |
| |
| |
| /* 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); |
| |
| if (ret != 0) { |
| err = ret; |
| goto errorout; |
| } |
| |
| DPRINT_DBG(VMBUS, "channel %p <relid %d gpadl 0x%x send ring %p " |
| "size %d recv ring %p size %d, downstreamoffset %d>", |
| 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(*openInfo) + |
| sizeof(struct vmbus_channel_open_channel), |
| GFP_KERNEL); |
| if (!openInfo) { |
| err = -ENOMEM; |
| goto errorout; |
| } |
| |
| openInfo->WaitEvent = osd_WaitEventCreate(); |
| if (!openInfo->WaitEvent) { |
| err = -ENOMEM; |
| goto errorout; |
| } |
| |
| openMsg = (struct 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; |
| openMsg->DownstreamRingBufferPageOffset = SendRingBufferSize >> |
| PAGE_SHIFT; |
| openMsg->ServerContextAreaGpadlHandle = 0; /* TODO */ |
| |
| if (UserDataLen > MAX_USER_DEFINED_BYTES) { |
| err = -EINVAL; |
| goto errorout; |
| } |
| |
| if (UserDataLen) |
| memcpy(openMsg->UserData, UserData, UserDataLen); |
| |
| spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags); |
| list_add_tail(&openInfo->MsgListEntry, |
| &gVmbusConnection.ChannelMsgList); |
| spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags); |
| |
| DPRINT_DBG(VMBUS, "Sending channel open msg..."); |
| |
| ret = VmbusPostMessage(openMsg, |
| sizeof(struct vmbus_channel_open_channel)); |
| if (ret != 0) { |
| DPRINT_ERR(VMBUS, "unable to open channel - %d", ret); |
| goto Cleanup; |
| } |
| |
| /* FIXME: Need to time-out here */ |
| osd_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); |
| list_del(&openInfo->MsgListEntry); |
| spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags); |
| |
| kfree(openInfo->WaitEvent); |
| kfree(openInfo); |
| |
| DPRINT_EXIT(VMBUS); |
| |
| return 0; |
| |
| errorout: |
| RingBufferCleanup(&NewChannel->Outbound); |
| RingBufferCleanup(&NewChannel->Inbound); |
| osd_PageFree(out, (SendRingBufferSize + RecvRingBufferSize) |
| >> PAGE_SHIFT); |
| kfree(openInfo); |
| return err; |
| } |
| |
| /* |
| * DumpGpadlBody - Dump the gpadl body message to the console for |
| * debugging purposes. |
| */ |
| static void DumpGpadlBody(struct vmbus_channel_gpadl_body *Gpadl, u32 Len) |
| { |
| int i; |
| int pfnCount; |
| |
| pfnCount = (Len - sizeof(struct 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]); |
| } |
| |
| /* |
| * DumpGpadlHeader - Dump the gpadl header message to the console for |
| * debugging purposes. |
| */ |
| static void DumpGpadlHeader(struct vmbus_channel_gpadl_header *Gpadl) |
| { |
| int i, j; |
| int pageCount; |
| |
| 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]); |
| } |
| } |
| |
| /* |
| * VmbusChannelCreateGpadlHeader - Creates a gpadl for the specified buffer |
| */ |
| static int VmbusChannelCreateGpadlHeader(void *Kbuffer, u32 Size, |
| struct vmbus_channel_msginfo **MsgInfo, |
| u32 *MessageCount) |
| { |
| int i; |
| int pageCount; |
| unsigned long long pfn; |
| struct vmbus_channel_gpadl_header *gpaHeader; |
| struct vmbus_channel_gpadl_body *gpadlBody; |
| struct vmbus_channel_msginfo *msgHeader; |
| struct vmbus_channel_msginfo *msgBody = NULL; |
| 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 = virt_to_phys(Kbuffer) >> PAGE_SHIFT; |
| |
| /* do we need a gpadl body msg */ |
| pfnSize = MAX_SIZE_CHANNEL_MESSAGE - |
| sizeof(struct vmbus_channel_gpadl_header) - |
| sizeof(struct gpa_range); |
| pfnCount = pfnSize / sizeof(u64); |
| |
| if (pageCount > pfnCount) { |
| /* we need a gpadl body */ |
| /* fill in the header */ |
| msgSize = sizeof(struct vmbus_channel_msginfo) + |
| sizeof(struct vmbus_channel_gpadl_header) + |
| sizeof(struct gpa_range) + pfnCount * sizeof(u64); |
| msgHeader = kzalloc(msgSize, GFP_KERNEL); |
| if (!msgHeader) |
| goto nomem; |
| |
| INIT_LIST_HEAD(&msgHeader->SubMsgList); |
| msgHeader->MessageSize = msgSize; |
| |
| gpaHeader = (struct vmbus_channel_gpadl_header *)msgHeader->Msg; |
| gpaHeader->RangeCount = 1; |
| gpaHeader->RangeBufLen = sizeof(struct gpa_range) + |
| pageCount * sizeof(u64); |
| gpaHeader->Range[0].ByteOffset = 0; |
| gpaHeader->Range[0].ByteCount = Size; |
| for (i = 0; i < pfnCount; i++) |
| gpaHeader->Range[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(struct vmbus_channel_gpadl_body); |
| pfnCount = pfnSize / sizeof(u64); |
| |
| /* fill in the body */ |
| while (pfnLeft) { |
| if (pfnLeft > pfnCount) |
| pfnCurr = pfnCount; |
| else |
| pfnCurr = pfnLeft; |
| |
| msgSize = sizeof(struct vmbus_channel_msginfo) + |
| sizeof(struct vmbus_channel_gpadl_body) + |
| pfnCurr * sizeof(u64); |
| msgBody = kzalloc(msgSize, GFP_KERNEL); |
| /* FIXME: we probably need to more if this fails */ |
| if (!msgBody) |
| goto nomem; |
| msgBody->MessageSize = msgSize; |
| (*MessageCount)++; |
| gpadlBody = |
| (struct 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; i < pfnCurr; i++) |
| gpadlBody->Pfn[i] = pfn + pfnSum + i; |
| |
| /* add to msg header */ |
| list_add_tail(&msgBody->MsgListEntry, |
| &msgHeader->SubMsgList); |
| pfnSum += pfnCurr; |
| pfnLeft -= pfnCurr; |
| } |
| } else { |
| /* everything fits in a header */ |
| msgSize = sizeof(struct vmbus_channel_msginfo) + |
| sizeof(struct vmbus_channel_gpadl_header) + |
| sizeof(struct gpa_range) + pageCount * sizeof(u64); |
| msgHeader = kzalloc(msgSize, GFP_KERNEL); |
| msgHeader->MessageSize = msgSize; |
| |
| gpaHeader = (struct vmbus_channel_gpadl_header *)msgHeader->Msg; |
| gpaHeader->RangeCount = 1; |
| gpaHeader->RangeBufLen = sizeof(struct gpa_range) + |
| pageCount * sizeof(u64); |
| gpaHeader->Range[0].ByteOffset = 0; |
| gpaHeader->Range[0].ByteCount = Size; |
| for (i = 0; i < pageCount; i++) |
| gpaHeader->Range[0].PfnArray[i] = pfn+i; |
| |
| *MsgInfo = msgHeader; |
| *MessageCount = 1; |
| } |
| |
| return 0; |
| nomem: |
| kfree(msgHeader); |
| kfree(msgBody); |
| return -ENOMEM; |
| } |
| |
| /* |
| * VmbusChannelEstablishGpadl - Estabish a GPADL for the specified buffer |
| * |
| * @Channel: a channel |
| * @Kbuffer: from kmalloc |
| * @Size: page-size multiple |
| * @GpadlHandle: some funky thing |
| */ |
| int VmbusChannelEstablishGpadl(struct vmbus_channel *Channel, void *Kbuffer, |
| u32 Size, u32 *GpadlHandle) |
| { |
| struct vmbus_channel_gpadl_header *gpadlMsg; |
| struct vmbus_channel_gpadl_body *gpadlBody; |
| /* struct vmbus_channel_gpadl_created *gpadlCreated; */ |
| struct vmbus_channel_msginfo *msgInfo = NULL; |
| struct vmbus_channel_msginfo *subMsgInfo; |
| u32 msgCount; |
| struct list_head *curr; |
| u32 nextGpadlHandle; |
| unsigned long flags; |
| int ret = 0; |
| |
| DPRINT_ENTER(VMBUS); |
| |
| nextGpadlHandle = atomic_read(&gVmbusConnection.NextGpadlHandle); |
| atomic_inc(&gVmbusConnection.NextGpadlHandle); |
| |
| ret = VmbusChannelCreateGpadlHeader(Kbuffer, Size, &msgInfo, &msgCount); |
| if (ret) |
| return ret; |
| |
| msgInfo->WaitEvent = osd_WaitEventCreate(); |
| if (!msgInfo->WaitEvent) { |
| ret = -ENOMEM; |
| goto Cleanup; |
| } |
| |
| gpadlMsg = (struct 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); |
| list_add_tail(&msgInfo->MsgListEntry, |
| &gVmbusConnection.ChannelMsgList); |
| |
| 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(*msgInfo)); |
| |
| ret = VmbusPostMessage(gpadlMsg, msgInfo->MessageSize - |
| sizeof(*msgInfo)); |
| if (ret != 0) { |
| DPRINT_ERR(VMBUS, "Unable to open channel - %d", ret); |
| goto Cleanup; |
| } |
| |
| if (msgCount > 1) { |
| list_for_each(curr, &msgInfo->SubMsgList) { |
| |
| /* FIXME: should this use list_entry() instead ? */ |
| subMsgInfo = (struct vmbus_channel_msginfo *)curr; |
| gpadlBody = |
| (struct vmbus_channel_gpadl_body *)subMsgInfo->Msg; |
| |
| gpadlBody->Header.MessageType = ChannelMessageGpadlBody; |
| gpadlBody->Gpadl = nextGpadlHandle; |
| |
| DPRINT_DBG(VMBUS, "Sending GPADL Body - len %zd", |
| subMsgInfo->MessageSize - |
| sizeof(*subMsgInfo)); |
| |
| DumpGpadlBody(gpadlBody, subMsgInfo->MessageSize - |
| sizeof(*subMsgInfo)); |
| ret = VmbusPostMessage(gpadlBody, |
| subMsgInfo->MessageSize - |
| sizeof(*subMsgInfo)); |
| if (ret != 0) |
| goto Cleanup; |
| |
| } |
| } |
| osd_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); |
| list_del(&msgInfo->MsgListEntry); |
| spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags); |
| |
| kfree(msgInfo->WaitEvent); |
| kfree(msgInfo); |
| |
| DPRINT_EXIT(VMBUS); |
| |
| return ret; |
| } |
| |
| /* |
| * VmbusChannelTeardownGpadl -Teardown the specified GPADL handle |
| */ |
| int VmbusChannelTeardownGpadl(struct vmbus_channel *Channel, u32 GpadlHandle) |
| { |
| struct vmbus_channel_gpadl_teardown *msg; |
| struct vmbus_channel_msginfo *info; |
| unsigned long flags; |
| int ret; |
| |
| DPRINT_ENTER(VMBUS); |
| |
| /* ASSERT(GpadlHandle != 0); */ |
| |
| info = kmalloc(sizeof(*info) + |
| sizeof(struct vmbus_channel_gpadl_teardown), GFP_KERNEL); |
| if (!info) |
| return -ENOMEM; |
| |
| info->WaitEvent = osd_WaitEventCreate(); |
| if (!info->WaitEvent) { |
| kfree(info); |
| return -ENOMEM; |
| } |
| |
| msg = (struct 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); |
| list_add_tail(&info->MsgListEntry, |
| &gVmbusConnection.ChannelMsgList); |
| spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags); |
| |
| ret = VmbusPostMessage(msg, |
| sizeof(struct vmbus_channel_gpadl_teardown)); |
| if (ret != 0) { |
| /* TODO: */ |
| /* something... */ |
| } |
| |
| osd_WaitEventWait(info->WaitEvent); |
| |
| /* Received a torndown response */ |
| spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags); |
| list_del(&info->MsgListEntry); |
| spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags); |
| |
| kfree(info->WaitEvent); |
| kfree(info); |
| |
| DPRINT_EXIT(VMBUS); |
| |
| return ret; |
| } |
| |
| /* |
| * VmbusChannelClose - Close the specified channel |
| */ |
| void VmbusChannelClose(struct vmbus_channel *Channel) |
| { |
| struct vmbus_channel_close_channel *msg; |
| struct vmbus_channel_msginfo *info; |
| unsigned long flags; |
| int ret; |
| |
| DPRINT_ENTER(VMBUS); |
| |
| /* Stop callback and cancel the timer asap */ |
| Channel->OnChannelCallback = NULL; |
| del_timer_sync(&Channel->poll_timer); |
| |
| /* Send a closing message */ |
| info = kmalloc(sizeof(*info) + |
| sizeof(struct vmbus_channel_close_channel), GFP_KERNEL); |
| /* FIXME: can't do anything other than return here because the |
| * function is void */ |
| if (!info) |
| return; |
| |
| /* info->waitEvent = osd_WaitEventCreate(); */ |
| |
| msg = (struct vmbus_channel_close_channel *)info->Msg; |
| msg->Header.MessageType = ChannelMessageCloseChannel; |
| msg->ChildRelId = Channel->OfferMsg.ChildRelId; |
| |
| ret = VmbusPostMessage(msg, sizeof(struct vmbus_channel_close_channel)); |
| if (ret != 0) { |
| /* TODO: */ |
| /* something... */ |
| } |
| |
| /* 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); |
| |
| osd_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); |
| list_del(&Channel->ListEntry); |
| spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags); |
| |
| FreeVmbusChannel(Channel); |
| } |
| |
| DPRINT_EXIT(VMBUS); |
| } |
| |
| /** |
| * VmbusChannelSendPacket() - Send the specified buffer on the given channel |
| * @Channel: Pointer to vmbus_channel structure. |
| * @Buffer: Pointer to the buffer you want to receive the data into. |
| * @BufferLen: Maximum size of what the the buffer will hold |
| * @RequestId: Identifier of the request |
| * @vmbus_packet_type: Type of packet that is being send e.g. negotiate, time |
| * packet etc. |
| * |
| * Sends data in @Buffer directly to hyper-v via the vmbus |
| * This will send the data unparsed to hyper-v. |
| * |
| * Mainly used by Hyper-V drivers. |
| */ |
| int VmbusChannelSendPacket(struct vmbus_channel *Channel, const void *Buffer, |
| u32 BufferLen, u64 RequestId, |
| enum vmbus_packet_type Type, u32 Flags) |
| { |
| struct vmpacket_descriptor desc; |
| u32 packetLen = sizeof(struct vmpacket_descriptor) + BufferLen; |
| u32 packetLenAligned = ALIGN_UP(packetLen, sizeof(u64)); |
| struct scatterlist bufferList[3]; |
| u64 alignedData = 0; |
| int ret; |
| |
| 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; */ |
| /* in 8-bytes granularity */ |
| desc.DataOffset8 = sizeof(struct vmpacket_descriptor) >> 3; |
| desc.Length8 = (u16)(packetLenAligned >> 3); |
| desc.TransactionId = RequestId; |
| |
| sg_init_table(bufferList, 3); |
| sg_set_buf(&bufferList[0], &desc, sizeof(struct vmpacket_descriptor)); |
| sg_set_buf(&bufferList[1], Buffer, BufferLen); |
| sg_set_buf(&bufferList[2], &alignedData, 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; |
| } |
| EXPORT_SYMBOL(VmbusChannelSendPacket); |
| |
| /* |
| * VmbusChannelSendPacketPageBuffer - Send a range of single-page buffer |
| * packets using a GPADL Direct packet type. |
| */ |
| int VmbusChannelSendPacketPageBuffer(struct vmbus_channel *Channel, |
| struct hv_page_buffer PageBuffers[], |
| u32 PageCount, void *Buffer, u32 BufferLen, |
| u64 RequestId) |
| { |
| int ret; |
| int i; |
| struct VMBUS_CHANNEL_PACKET_PAGE_BUFFER desc; |
| u32 descSize; |
| u32 packetLen; |
| u32 packetLenAligned; |
| struct scatterlist bufferList[3]; |
| u64 alignedData = 0; |
| |
| DPRINT_ENTER(VMBUS); |
| |
| if (PageCount > MAX_PAGE_BUFFER_COUNT) |
| return -EINVAL; |
| |
| 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(struct hv_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; i < PageCount; i++) { |
| desc.Range[i].Length = PageBuffers[i].Length; |
| desc.Range[i].Offset = PageBuffers[i].Offset; |
| desc.Range[i].Pfn = PageBuffers[i].Pfn; |
| } |
| |
| sg_init_table(bufferList, 3); |
| sg_set_buf(&bufferList[0], &desc, descSize); |
| sg_set_buf(&bufferList[1], Buffer, BufferLen); |
| sg_set_buf(&bufferList[2], &alignedData, 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; |
| } |
| |
| /* |
| * VmbusChannelSendPacketMultiPageBuffer - Send a multi-page buffer packet |
| * using a GPADL Direct packet type. |
| */ |
| int VmbusChannelSendPacketMultiPageBuffer(struct vmbus_channel *Channel, |
| struct hv_multipage_buffer *MultiPageBuffer, |
| void *Buffer, u32 BufferLen, u64 RequestId) |
| { |
| int ret; |
| struct VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER desc; |
| u32 descSize; |
| u32 packetLen; |
| u32 packetLenAligned; |
| struct scatterlist 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); |
| |
| if ((PfnCount < 0) || (PfnCount > MAX_MULTIPAGE_BUFFER_COUNT)) |
| return -EINVAL; |
| |
| /* |
| * 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)); |
| |
| sg_init_table(bufferList, 3); |
| sg_set_buf(&bufferList[0], &desc, descSize); |
| sg_set_buf(&bufferList[1], Buffer, BufferLen); |
| sg_set_buf(&bufferList[2], &alignedData, 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; |
| } |
| |
| |
| /** |
| * VmbusChannelRecvPacket() - Retrieve the user packet on the specified channel |
| * @Channel: Pointer to vmbus_channel structure. |
| * @Buffer: Pointer to the buffer you want to receive the data into. |
| * @BufferLen: Maximum size of what the the buffer will hold |
| * @BufferActualLen: The actual size of the data after it was received |
| * @RequestId: Identifier of the request |
| * |
| * Receives directly from the hyper-v vmbus and puts the data it received |
| * into Buffer. This will receive the data unparsed from hyper-v. |
| * |
| * Mainly used by Hyper-V drivers. |
| */ |
| int VmbusChannelRecvPacket(struct vmbus_channel *Channel, void *Buffer, |
| u32 BufferLen, u32 *BufferActualLen, u64 *RequestId) |
| { |
| struct 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(struct 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 <type %d " |
| "flag %d tid %llx pktlen %d datalen %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; |
| } |
| EXPORT_SYMBOL(VmbusChannelRecvPacket); |
| |
| /* |
| * VmbusChannelRecvPacketRaw - Retrieve the raw packet on the specified channel |
| */ |
| int VmbusChannelRecvPacketRaw(struct vmbus_channel *Channel, void *Buffer, |
| u32 BufferLen, u32 *BufferActualLen, |
| u64 *RequestId) |
| { |
| struct 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(struct 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 <type %d " |
| "flag %d tid %llx pktlen %d datalen %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; |
| } |
| |
| /* |
| * VmbusChannelOnChannelEvent - Channel event callback |
| */ |
| void VmbusChannelOnChannelEvent(struct vmbus_channel *Channel) |
| { |
| DumpVmbusChannel(Channel); |
| /* ASSERT(Channel->OnChannelCallback); */ |
| |
| Channel->OnChannelCallback(Channel->ChannelCallbackContext); |
| |
| mod_timer(&Channel->poll_timer, jiffies + usecs_to_jiffies(100)); |
| } |
| |
| /* |
| * VmbusChannelOnTimer - Timer event callback |
| */ |
| void VmbusChannelOnTimer(unsigned long data) |
| { |
| struct vmbus_channel *channel = (struct vmbus_channel *)data; |
| |
| if (channel->OnChannelCallback) |
| channel->OnChannelCallback(channel->ChannelCallbackContext); |
| } |
| |
| /* |
| * DumpVmbusChannel - Dump vmbus channel info to the console |
| */ |
| static void DumpVmbusChannel(struct vmbus_channel *Channel) |
| { |
| DPRINT_DBG(VMBUS, "Channel (%d)", Channel->OfferMsg.ChildRelId); |
| DumpRingInfo(&Channel->Outbound, "Outbound "); |
| DumpRingInfo(&Channel->Inbound, "Inbound "); |
| } |