| summaryrefslogtreecommitdiff |
| author | Olaf Hering <ohering@suse.de> | 2011-06-15 18:58:57 (GMT) |
|---|---|---|
| committer | Olaf Hering <ohering@suse.de> | 2011-06-15 18:58:57 (GMT) |
| commit | 88265a8485765665958b7218808c7f30be5b102e (patch) (side-by-side diff) | |
| tree | b67c842bf8f78d80ed99ade40fc878bc7607caaf | |
| parent | c63333ec3dfd8bf4dea6f178e57b4f0e529aeb0a (diff) | |
- patches.suse/staging-hv-update-to-3.0.patchrpm-2.6.32.41-0.5
Update hyperv drivers to current mainline state (bnc#662432).
- patches.xen/xen3-patch-2.6.32: Refresh.
suse-commit: 131ee6958a09a04cdde272d9c67a1089b77638c1
63 files changed, 11811 insertions, 13454 deletions
diff --git a/drivers/staging/hv/BlkVsc.c b/drivers/staging/hv/BlkVsc.c deleted file mode 100644 index ee7c298..0000000 --- a/drivers/staging/hv/BlkVsc.c +++ b/dev/null @@ -1,102 +0,0 @@ -/* - * - * 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 "osd.h" -#include "StorVsc.c" - -static const char *gBlkDriverName = "blkvsc"; - -/* {32412632-86cb-44a2-9b5c-50d1417354f5} */ -static const struct hv_guid gBlkVscDeviceType = { - .data = { - 0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, - 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5 - } -}; - -static int BlkVscOnDeviceAdd(struct hv_device *Device, void *AdditionalInfo) -{ - struct storvsc_device_info *deviceInfo; - int ret = 0; - - deviceInfo = (struct storvsc_device_info *)AdditionalInfo; - - ret = StorVscOnDeviceAdd(Device, AdditionalInfo); - if (ret != 0) - return ret; - - /* - * We need to use the device instance guid to set the path and target - * id. For IDE devices, the device instance id is formatted as - * <bus id> * - <device id> - 8899 - 000000000000. - */ - deviceInfo->PathId = Device->deviceInstance.data[3] << 24 | - Device->deviceInstance.data[2] << 16 | - Device->deviceInstance.data[1] << 8 | - Device->deviceInstance.data[0]; - - deviceInfo->TargetId = Device->deviceInstance.data[5] << 8 | - Device->deviceInstance.data[4]; - - return ret; -} - -int BlkVscInitialize(struct hv_driver *Driver) -{ - struct storvsc_driver_object *storDriver; - int ret = 0; - - storDriver = (struct storvsc_driver_object *)Driver; - - /* Make sure we are at least 2 pages since 1 page is used for control */ - /* ASSERT(storDriver->RingBufferSize >= (PAGE_SIZE << 1)); */ - - Driver->name = gBlkDriverName; - memcpy(&Driver->deviceType, &gBlkVscDeviceType, sizeof(struct hv_guid)); - - storDriver->RequestExtSize = sizeof(struct storvsc_request_extension); - - /* - * Divide the ring buffer data size (which is 1 page less than the ring - * buffer size since that page is reserved for the ring buffer indices) - * by the max request size (which is - * VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER + struct vstor_packet + u64) - */ - storDriver->MaxOutstandingRequestsPerChannel = - ((storDriver->RingBufferSize - PAGE_SIZE) / - ALIGN_UP(MAX_MULTIPAGE_BUFFER_PACKET + - sizeof(struct vstor_packet) + sizeof(u64), - sizeof(u64))); - - DPRINT_INFO(BLKVSC, "max io outstd %u", - storDriver->MaxOutstandingRequestsPerChannel); - - /* Setup the dispatch table */ - storDriver->Base.OnDeviceAdd = BlkVscOnDeviceAdd; - storDriver->Base.OnDeviceRemove = StorVscOnDeviceRemove; - storDriver->Base.OnCleanup = StorVscOnCleanup; - storDriver->OnIORequest = StorVscOnIORequest; - - return ret; -} diff --git a/drivers/staging/hv/Channel.c b/drivers/staging/hv/Channel.c deleted file mode 100644 index d712ba8..0000000 --- a/drivers/staging/hv/Channel.c +++ b/dev/null @@ -1,1044 +0,0 @@ -/* - * 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 "VmbusPrivate.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; - - if (Channel->OfferMsg.MonitorAllocated) { - /* Each u32 represents 32 channels */ - sync_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 */ - - sync_set_bit(Channel->MonitorBit, - (unsigned long *)&monitorPage->TriggerGroup - [Channel->MonitorGroup].Pending); - - } else { - VmbusSetEvent(Channel->OfferMsg.ChildRelId); - } -} - -#if 0 -static void VmbusChannelClearEvent(struct vmbus_channel *channel) -{ - struct hv_monitor_page *monitorPage; - - if (Channel->OfferMsg.MonitorAllocated) { - /* Each u32 represents 32 channels */ - sync_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 */ - - sync_clear_bit(Channel->MonitorBit, - (unsigned long *)&monitorPage->TriggerGroup - [Channel->MonitorGroup].Pending); - } -} - -#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; - - /* 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); - 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); - if (msgHeader == NULL) - goto nomem; - 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; - - 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); - 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; - - /* 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); - 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; - - /* 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); - } -} - -/** - * 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_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); - - 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; - - 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); - - 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); - - 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); - - 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; - - *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!!"); */ - 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); - 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); - - 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; - - *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!!"); */ - 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); - 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); - 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 "); -} diff --git a/drivers/staging/hv/Channel.h b/drivers/staging/hv/Channel.h deleted file mode 100644 index 6b283ed..0000000 --- a/drivers/staging/hv/Channel.h +++ b/dev/null @@ -1,112 +0,0 @@ -/* - * - * 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> - * - */ - - -#ifndef _CHANNEL_H_ -#define _CHANNEL_H_ - -#include "ChannelMgmt.h" - -/* The format must be the same as struct vmdata_gpa_direct */ -struct VMBUS_CHANNEL_PACKET_PAGE_BUFFER { - u16 Type; - u16 DataOffset8; - u16 Length8; - u16 Flags; - u64 TransactionId; - u32 Reserved; - u32 RangeCount; - struct hv_page_buffer Range[MAX_PAGE_BUFFER_COUNT]; -} __attribute__((packed)); - -/* The format must be the same as struct vmdata_gpa_direct */ -struct VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER { - u16 Type; - u16 DataOffset8; - u16 Length8; - u16 Flags; - u64 TransactionId; - u32 Reserved; - u32 RangeCount; /* Always 1 in this case */ - struct hv_multipage_buffer Range; -} __attribute__((packed)); - - -extern int VmbusChannelOpen(struct vmbus_channel *channel, - u32 SendRingBufferSize, - u32 RecvRingBufferSize, - void *UserData, - u32 UserDataLen, - void(*OnChannelCallback)(void *context), - void *Context); - -extern void VmbusChannelClose(struct vmbus_channel *channel); - -extern int VmbusChannelSendPacket(struct vmbus_channel *channel, - const void *Buffer, - u32 BufferLen, - u64 RequestId, - enum vmbus_packet_type Type, - u32 Flags); - -extern int VmbusChannelSendPacketPageBuffer(struct vmbus_channel *channel, - struct hv_page_buffer PageBuffers[], - u32 PageCount, - void *Buffer, - u32 BufferLen, - u64 RequestId); - -extern int VmbusChannelSendPacketMultiPageBuffer(struct vmbus_channel *channel, - struct hv_multipage_buffer *mpb, - void *Buffer, - u32 BufferLen, - u64 RequestId); - -extern int VmbusChannelEstablishGpadl(struct vmbus_channel *channel, - void *Kbuffer, - u32 Size, - u32 *GpadlHandle); - -extern int VmbusChannelTeardownGpadl(struct vmbus_channel *channel, - u32 GpadlHandle); - -extern int VmbusChannelRecvPacket(struct vmbus_channel *channel, - void *Buffer, - u32 BufferLen, - u32 *BufferActualLen, - u64 *RequestId); - -extern int VmbusChannelRecvPacketRaw(struct vmbus_channel *channel, - void *Buffer, - u32 BufferLen, - u32 *BufferActualLen, - u64 *RequestId); - -extern void VmbusChannelOnChannelEvent(struct vmbus_channel *channel); - -extern void VmbusChannelGetDebugInfo(struct vmbus_channel *channel, - struct vmbus_channel_debug_info *debug); - -extern void VmbusChannelOnTimer(unsigned long data); - -#endif /* _CHANNEL_H_ */ diff --git a/drivers/staging/hv/ChannelInterface.c b/drivers/staging/hv/ChannelInterface.c deleted file mode 100644 index 019b064..0000000 --- a/drivers/staging/hv/ChannelInterface.c +++ b/dev/null @@ -1,152 +0,0 @@ -/* - * - * 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 "osd.h" -#include "VmbusPrivate.h" - -static int IVmbusChannelOpen(struct hv_device *device, u32 SendBufferSize, - u32 RecvRingBufferSize, void *UserData, - u32 UserDataLen, - void (*ChannelCallback)(void *context), - void *Context) -{ - return VmbusChannelOpen(device->context, SendBufferSize, - RecvRingBufferSize, UserData, UserDataLen, - ChannelCallback, Context); -} - -static void IVmbusChannelClose(struct hv_device *device) -{ - VmbusChannelClose(device->context); -} - -static int IVmbusChannelSendPacket(struct hv_device *device, const void *Buffer, - u32 BufferLen, u64 RequestId, u32 Type, - u32 Flags) -{ - return VmbusChannelSendPacket(device->context, Buffer, BufferLen, - RequestId, Type, Flags); -} - -static int IVmbusChannelSendPacketPageBuffer(struct hv_device *device, - struct hv_page_buffer PageBuffers[], - u32 PageCount, void *Buffer, - u32 BufferLen, u64 RequestId) -{ - return VmbusChannelSendPacketPageBuffer(device->context, PageBuffers, - PageCount, Buffer, BufferLen, - RequestId); -} - -static int IVmbusChannelSendPacketMultiPageBuffer(struct hv_device *device, - struct hv_multipage_buffer *MultiPageBuffer, - void *Buffer, u32 BufferLen, u64 RequestId) -{ - return VmbusChannelSendPacketMultiPageBuffer(device->context, - MultiPageBuffer, Buffer, - BufferLen, RequestId); -} - -static int IVmbusChannelRecvPacket(struct hv_device *device, void *Buffer, - u32 BufferLen, u32 *BufferActualLen, - u64 *RequestId) -{ - return VmbusChannelRecvPacket(device->context, Buffer, BufferLen, - BufferActualLen, RequestId); -} - -static int IVmbusChannelRecvPacketRaw(struct hv_device *device, void *Buffer, - u32 BufferLen, u32 *BufferActualLen, - u64 *RequestId) -{ - return VmbusChannelRecvPacketRaw(device->context, Buffer, BufferLen, - BufferActualLen, RequestId); -} - -static int IVmbusChannelEstablishGpadl(struct hv_device *device, void *Buffer, - u32 BufferLen, u32 *GpadlHandle) -{ - return VmbusChannelEstablishGpadl(device->context, Buffer, BufferLen, - GpadlHandle); -} - -static int IVmbusChannelTeardownGpadl(struct hv_device *device, u32 GpadlHandle) -{ - return VmbusChannelTeardownGpadl(device->context, GpadlHandle); - -} - -void GetChannelInterface(struct vmbus_channel_interface *iface) -{ - iface->Open = IVmbusChannelOpen; - iface->Close = IVmbusChannelClose; - iface->SendPacket = IVmbusChannelSendPacket; - iface->SendPacketPageBuffer = IVmbusChannelSendPacketPageBuffer; - iface->SendPacketMultiPageBuffer = - IVmbusChannelSendPacketMultiPageBuffer; - iface->RecvPacket = IVmbusChannelRecvPacket; - iface->RecvPacketRaw = IVmbusChannelRecvPacketRaw; - iface->EstablishGpadl = IVmbusChannelEstablishGpadl; - iface->TeardownGpadl = IVmbusChannelTeardownGpadl; - iface->GetInfo = GetChannelInfo; -} - -void GetChannelInfo(struct hv_device *device, struct hv_device_info *info) -{ - struct vmbus_channel_debug_info debugInfo; - - if (!device->context) - return; - - VmbusChannelGetDebugInfo(device->context, &debugInfo); - - info->ChannelId = debugInfo.RelId; - info->ChannelState = debugInfo.State; - memcpy(&info->ChannelType, &debugInfo.InterfaceType, - sizeof(struct hv_guid)); - memcpy(&info->ChannelInstance, &debugInfo.InterfaceInstance, - sizeof(struct hv_guid)); - - info->MonitorId = debugInfo.MonitorId; - - info->ServerMonitorPending = debugInfo.ServerMonitorPending; - info->ServerMonitorLatency = debugInfo.ServerMonitorLatency; - info->ServerMonitorConnectionId = debugInfo.ServerMonitorConnectionId; - - info->ClientMonitorPending = debugInfo.ClientMonitorPending; - info->ClientMonitorLatency = debugInfo.ClientMonitorLatency; - info->ClientMonitorConnectionId = debugInfo.ClientMonitorConnectionId; - - info->Inbound.InterruptMask = debugInfo.Inbound.CurrentInterruptMask; - info->Inbound.ReadIndex = debugInfo.Inbound.CurrentReadIndex; - info->Inbound.WriteIndex = debugInfo.Inbound.CurrentWriteIndex; - info->Inbound.BytesAvailToRead = debugInfo.Inbound.BytesAvailToRead; - info->Inbound.BytesAvailToWrite = debugInfo.Inbound.BytesAvailToWrite; - - info->Outbound.InterruptMask = debugInfo.Outbound.CurrentInterruptMask; - info->Outbound.ReadIndex = debugInfo.Outbound.CurrentReadIndex; - info->Outbound.WriteIndex = debugInfo.Outbound.CurrentWriteIndex; - info->Outbound.BytesAvailToRead = debugInfo.Outbound.BytesAvailToRead; - info->Outbound.BytesAvailToWrite = debugInfo.Outbound.BytesAvailToWrite; -} diff --git a/drivers/staging/hv/ChannelInterface.h b/drivers/staging/hv/ChannelInterface.h deleted file mode 100644 index 27b7a25..0000000 --- a/drivers/staging/hv/ChannelInterface.h +++ b/dev/null @@ -1,35 +0,0 @@ -/* - * - * 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> - * - */ - - -#ifndef _CHANNEL_INTERFACE_H_ -#define _CHANNEL_INTERFACE_H_ - -#include "VmbusApi.h" - -void GetChannelInterface(struct vmbus_channel_interface *ChannelInterface); - -void GetChannelInfo(struct hv_device *Device, - struct hv_device_info *DeviceInfo); - -#endif /* _CHANNEL_INTERFACE_H_ */ diff --git a/drivers/staging/hv/ChannelMgmt.c b/drivers/staging/hv/ChannelMgmt.c deleted file mode 100644 index 8d27f70..0000000 --- a/drivers/staging/hv/ChannelMgmt.c +++ b/dev/null @@ -1,851 +0,0 @@ -/* - * 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/list.h> -#include <linux/module.h> -#include <linux/completion.h> -#include "osd.h" -#include "logging.h" -#include "VmbusPrivate.h" -#include "utils.h" - -struct vmbus_channel_message_table_entry { - enum vmbus_channel_message_type messageType; - void (*messageHandler)(struct vmbus_channel_message_header *msg); -}; - -#define MAX_MSG_TYPES 3 -#define MAX_NUM_DEVICE_CLASSES_SUPPORTED 7 - -static const struct hv_guid - gSupportedDeviceClasses[MAX_NUM_DEVICE_CLASSES_SUPPORTED] = { - /* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */ - /* Storage - SCSI */ - { - .data = { - 0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, - 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f - } - }, - - /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */ - /* Network */ - { - .data = { - 0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, - 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E - } - }, - - /* {CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A} */ - /* Input */ - { - .data = { - 0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, - 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A - } - }, - - /* {32412632-86cb-44a2-9b5c-50d1417354f5} */ - /* IDE */ - { - .data = { - 0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, - 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5 - } - }, - /* 0E0B6031-5213-4934-818B-38D90CED39DB */ - /* Shutdown */ - { - .data = { - 0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, - 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB - } - }, - /* {9527E630-D0AE-497b-ADCE-E80AB0175CAF} */ - /* TimeSync */ - { - .data = { - 0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, - 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf - } - }, - /* {57164f39-9115-4e78-ab55-382f3bd5422d} */ - /* Heartbeat */ - { - .data = { - 0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, - 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d - } - }, -}; - - -/** - * prep_negotiate_resp() - Create default response for Hyper-V Negotiate message - * @icmsghdrp: Pointer to msg header structure - * @icmsg_negotiate: Pointer to negotiate message structure - * @buf: Raw buffer channel data - * - * @icmsghdrp is of type &struct icmsg_hdr. - * @negop is of type &struct icmsg_negotiate. - * Set up and fill in default negotiate response message. This response can - * come from both the vmbus driver and the hv_utils driver. The current api - * will respond properly to both Windows 2008 and Windows 2008-R2 operating - * systems. - * - * Mainly used by Hyper-V drivers. - */ -void prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, - struct icmsg_negotiate *negop, - u8 *buf) -{ - if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - icmsghdrp->icmsgsize = 0x10; - - negop = (struct icmsg_negotiate *)&buf[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - - if (negop->icframe_vercnt == 2 && - negop->icversion_data[1].major == 3) { - negop->icversion_data[0].major = 3; - negop->icversion_data[0].minor = 0; - negop->icversion_data[1].major = 3; - negop->icversion_data[1].minor = 0; - } else { - negop->icversion_data[0].major = 1; - negop->icversion_data[0].minor = 0; - negop->icversion_data[1].major = 1; - negop->icversion_data[1].minor = 0; - } - - negop->icframe_vercnt = 1; - negop->icmsg_vercnt = 1; - } -} -EXPORT_SYMBOL(prep_negotiate_resp); - -/** - * chn_cb_negotiate() - Default handler for non IDE/SCSI/NETWORK - * Hyper-V requests - * @context: Pointer to argument structure. - * - * Set up the default handler for non device driver specific requests - * from Hyper-V. This stub responds to the default negotiate messages - * that come in for every non IDE/SCSI/Network request. - * This behavior is normally overwritten in the hv_utils driver. That - * driver handles requests like gracefull shutdown, heartbeats etc. - * - * Mainly used by Hyper-V drivers. - */ -void chn_cb_negotiate(void *context) -{ - struct vmbus_channel *channel = context; - u8 *buf; - u32 buflen, recvlen; - u64 requestid; - - struct icmsg_hdr *icmsghdrp; - struct icmsg_negotiate *negop = NULL; - - buflen = PAGE_SIZE; - buf = kmalloc(buflen, GFP_ATOMIC); - - VmbusChannelRecvPacket(channel, buf, buflen, &recvlen, &requestid); - - if (recvlen > 0) { - icmsghdrp = (struct icmsg_hdr *)&buf[ - sizeof(struct vmbuspipe_hdr)]; - - prep_negotiate_resp(icmsghdrp, negop, buf); - - icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION - | ICMSGHDRFLAG_RESPONSE; - - VmbusChannelSendPacket(channel, buf, - recvlen, requestid, - VmbusPacketTypeDataInBand, 0); - } - - kfree(buf); -} -EXPORT_SYMBOL(chn_cb_negotiate); - -/* - * Function table used for message responses for non IDE/SCSI/Network type - * messages. (Such as KVP/Shutdown etc) - */ -struct hyperv_service_callback hv_cb_utils[MAX_MSG_TYPES] = { - /* 0E0B6031-5213-4934-818B-38D90CED39DB */ - /* Shutdown */ - { - .msg_type = HV_SHUTDOWN_MSG, - .data = { - 0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, - 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB - }, - .callback = chn_cb_negotiate, - .log_msg = "Shutdown channel functionality initialized" - }, - - /* {9527E630-D0AE-497b-ADCE-E80AB0175CAF} */ - /* TimeSync */ - { - .msg_type = HV_TIMESYNC_MSG, - .data = { - 0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, - 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf - }, - .callback = chn_cb_negotiate, - .log_msg = "Timesync channel functionality initialized" - }, - /* {57164f39-9115-4e78-ab55-382f3bd5422d} */ - /* Heartbeat */ - { - .msg_type = HV_HEARTBEAT_MSG, - .data = { - 0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, - 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d - }, - .callback = chn_cb_negotiate, - .log_msg = "Heartbeat channel functionality initialized" - }, -}; -EXPORT_SYMBOL(hv_cb_utils); - -/* - * AllocVmbusChannel - Allocate and initialize a vmbus channel object - */ -struct vmbus_channel *AllocVmbusChannel(void) -{ - struct vmbus_channel *channel; - - channel = kzalloc(sizeof(*channel), GFP_ATOMIC); - if (!channel) - return NULL; - - spin_lock_init(&channel->inbound_lock); - - init_timer(&channel->poll_timer); - channel->poll_timer.data = (unsigned long)channel; - channel->poll_timer.function = VmbusChannelOnTimer; - - channel->ControlWQ = create_workqueue("hv_vmbus_ctl"); - if (!channel->ControlWQ) { - kfree(channel); - return NULL; - } - - return channel; -} - -/* - * ReleaseVmbusChannel - Release the vmbus channel object itself - */ -static inline void ReleaseVmbusChannel(void *context) -{ - struct vmbus_channel *channel = context; - - DPRINT_DBG(VMBUS, "releasing channel (%p)", channel); - destroy_workqueue(channel->ControlWQ); - DPRINT_DBG(VMBUS, "channel released (%p)", channel); - - kfree(channel); -} - -/* - * FreeVmbusChannel - Release the resources used by the vmbus channel object - */ -void FreeVmbusChannel(struct vmbus_channel *Channel) -{ - del_timer_sync(&Channel->poll_timer); - - /* - * We have to release the channel's workqueue/thread in the vmbus's - * workqueue/thread context - * ie we can't destroy ourselves. - */ - osd_schedule_callback(gVmbusConnection.WorkQueue, ReleaseVmbusChannel, - Channel); -} - - -DECLARE_COMPLETION(hv_channel_ready); - -/* - * Count initialized channels, and ensure all channels are ready when hv_vmbus - * module loading completes. - */ -static void count_hv_channel(void) -{ - static int counter; - unsigned long flags; - - spin_lock_irqsave(&gVmbusConnection.channel_lock, flags); - if (++counter == MAX_MSG_TYPES) - complete(&hv_channel_ready); - spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags); -} - - -/* - * VmbusChannelProcessOffer - Process the offer by creating a channel/device - * associated with this offer - */ -static void VmbusChannelProcessOffer(void *context) -{ - struct vmbus_channel *newChannel = context; - struct vmbus_channel *channel; - bool fNew = true; - int ret; - int cnt; - unsigned long flags; - - /* Make sure this is a new offer */ - spin_lock_irqsave(&gVmbusConnection.channel_lock, flags); - - list_for_each_entry(channel, &gVmbusConnection.ChannelList, ListEntry) { - if (!memcmp(&channel->OfferMsg.Offer.InterfaceType, - &newChannel->OfferMsg.Offer.InterfaceType, - sizeof(struct hv_guid)) && - !memcmp(&channel->OfferMsg.Offer.InterfaceInstance, - &newChannel->OfferMsg.Offer.InterfaceInstance, - sizeof(struct hv_guid))) { - fNew = false; - break; - } - } - - if (fNew) - list_add_tail(&newChannel->ListEntry, - &gVmbusConnection.ChannelList); - - spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags); - - if (!fNew) { - DPRINT_DBG(VMBUS, "Ignoring duplicate offer for relid (%d)", - newChannel->OfferMsg.ChildRelId); - FreeVmbusChannel(newChannel); - return; - } - - /* - * Start the process of binding this offer to the driver - * We need to set the DeviceObject field before calling - * VmbusChildDeviceAdd() - */ - newChannel->DeviceObject = VmbusChildDeviceCreate( - &newChannel->OfferMsg.Offer.InterfaceType, - &newChannel->OfferMsg.Offer.InterfaceInstance, - newChannel); - - DPRINT_DBG(VMBUS, "child device object allocated - %p", - newChannel->DeviceObject); - - /* - * Add the new device to the bus. This will kick off device-driver - * binding which eventually invokes the device driver's AddDevice() - * method. - */ - ret = VmbusChildDeviceAdd(newChannel->DeviceObject); - if (ret != 0) { - DPRINT_ERR(VMBUS, - "unable to add child device object (relid %d)", - newChannel->OfferMsg.ChildRelId); - - spin_lock_irqsave(&gVmbusConnection.channel_lock, flags); - list_del(&newChannel->ListEntry); - spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags); - - FreeVmbusChannel(newChannel); - } else { - /* - * This state is used to indicate a successful open - * so that when we do close the channel normally, we - * can cleanup properly - */ - newChannel->State = CHANNEL_OPEN_STATE; - - /* Open IC channels */ - for (cnt = 0; cnt < MAX_MSG_TYPES; cnt++) { - if (memcmp(&newChannel->OfferMsg.Offer.InterfaceType, - &hv_cb_utils[cnt].data, - sizeof(struct hv_guid)) == 0 && - VmbusChannelOpen(newChannel, 2 * PAGE_SIZE, - 2 * PAGE_SIZE, NULL, 0, - hv_cb_utils[cnt].callback, - newChannel) == 0) { - hv_cb_utils[cnt].channel = newChannel; - DPRINT_INFO(VMBUS, "%s", - hv_cb_utils[cnt].log_msg); - count_hv_channel(); - } - } - } -} - -/* - * VmbusChannelProcessRescindOffer - Rescind the offer by initiating a device removal - */ -static void VmbusChannelProcessRescindOffer(void *context) -{ - struct vmbus_channel *channel = context; - - VmbusChildDeviceRemove(channel->DeviceObject); -} - -/* - * VmbusChannelOnOffer - Handler for channel offers from vmbus in parent partition. - * - * We ignore all offers except network and storage offers. For each network and - * storage offers, we create a channel object and queue a work item to the - * channel object to process the offer synchronously - */ -static void VmbusChannelOnOffer(struct vmbus_channel_message_header *hdr) -{ - struct vmbus_channel_offer_channel *offer; - struct vmbus_channel *newChannel; - struct hv_guid *guidType; - struct hv_guid *guidInstance; - int i; - int fSupported = 0; - - offer = (struct vmbus_channel_offer_channel *)hdr; - for (i = 0; i < MAX_NUM_DEVICE_CLASSES_SUPPORTED; i++) { - if (memcmp(&offer->Offer.InterfaceType, - &gSupportedDeviceClasses[i], sizeof(struct hv_guid)) == 0) { - fSupported = 1; - break; - } - } - - if (!fSupported) { - DPRINT_DBG(VMBUS, "Ignoring channel offer notification for " - "child relid %d", offer->ChildRelId); - return; - } - - guidType = &offer->Offer.InterfaceType; - guidInstance = &offer->Offer.InterfaceInstance; - - DPRINT_INFO(VMBUS, "Channel offer notification - " - "child relid %d monitor id %d allocated %d, " - "type {%02x%02x%02x%02x-%02x%02x-%02x%02x-" - "%02x%02x%02x%02x%02x%02x%02x%02x} " - "instance {%02x%02x%02x%02x-%02x%02x-%02x%02x-" - "%02x%02x%02x%02x%02x%02x%02x%02x}", - offer->ChildRelId, offer->MonitorId, - offer->MonitorAllocated, - guidType->data[3], guidType->data[2], - guidType->data[1], guidType->data[0], - guidType->data[5], guidType->data[4], - guidType->data[7], guidType->data[6], - guidType->data[8], guidType->data[9], - guidType->data[10], guidType->data[11], - guidType->data[12], guidType->data[13], - guidType->data[14], guidType->data[15], - guidInstance->data[3], guidInstance->data[2], - guidInstance->data[1], guidInstance->data[0], - guidInstance->data[5], guidInstance->data[4], - guidInstance->data[7], guidInstance->data[6], - guidInstance->data[8], guidInstance->data[9], - guidInstance->data[10], guidInstance->data[11], - guidInstance->data[12], guidInstance->data[13], - guidInstance->data[14], guidInstance->data[15]); - - /* Allocate the channel object and save this offer. */ - newChannel = AllocVmbusChannel(); - if (!newChannel) { - DPRINT_ERR(VMBUS, "unable to allocate channel object"); - return; - } - - DPRINT_DBG(VMBUS, "channel object allocated - %p", newChannel); - - memcpy(&newChannel->OfferMsg, offer, - sizeof(struct vmbus_channel_offer_channel)); - newChannel->MonitorGroup = (u8)offer->MonitorId / 32; - newChannel->MonitorBit = (u8)offer->MonitorId % 32; - - /* TODO: Make sure the offer comes from our parent partition */ - osd_schedule_callback(newChannel->ControlWQ, VmbusChannelProcessOffer, - newChannel); -} - -/* - * VmbusChannelOnOfferRescind - Rescind offer handler. - * - * We queue a work item to process this offer synchronously - */ -static void VmbusChannelOnOfferRescind(struct vmbus_channel_message_header *hdr) -{ - struct vmbus_channel_rescind_offer *rescind; - struct vmbus_channel *channel; - - rescind = (struct vmbus_channel_rescind_offer *)hdr; - channel = GetChannelFromRelId(rescind->ChildRelId); - if (channel == NULL) { - DPRINT_DBG(VMBUS, "channel not found for relId %d", - rescind->ChildRelId); - return; - } - - osd_schedule_callback(channel->ControlWQ, - VmbusChannelProcessRescindOffer, - channel); -} - -/* - * VmbusChannelOnOffersDelivered - This is invoked when all offers have been delivered. - * - * Nothing to do here. - */ -static void VmbusChannelOnOffersDelivered( - struct vmbus_channel_message_header *hdr) -{ -} - -/* - * VmbusChannelOnOpenResult - Open result handler. - * - * This is invoked when we received a response to our channel open request. - * Find the matching request, copy the response and signal the requesting - * thread. - */ -static void VmbusChannelOnOpenResult(struct vmbus_channel_message_header *hdr) -{ - struct vmbus_channel_open_result *result; - struct list_head *curr; - struct vmbus_channel_msginfo *msgInfo; - struct vmbus_channel_message_header *requestHeader; - struct vmbus_channel_open_channel *openMsg; - unsigned long flags; - - result = (struct vmbus_channel_open_result *)hdr; - DPRINT_DBG(VMBUS, "vmbus open result - %d", result->Status); - - /* - * Find the open msg, copy the result and signal/unblock the wait event - */ - spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags); - - list_for_each(curr, &gVmbusConnection.ChannelMsgList) { -/* FIXME: this should probably use list_entry() instead */ - msgInfo = (struct vmbus_channel_msginfo *)curr; - requestHeader = (struct vmbus_channel_message_header *)msgInfo->Msg; - - if (requestHeader->MessageType == ChannelMessageOpenChannel) { - openMsg = (struct vmbus_channel_open_channel *)msgInfo->Msg; - if (openMsg->ChildRelId == result->ChildRelId && - openMsg->OpenId == result->OpenId) { - memcpy(&msgInfo->Response.OpenResult, - result, - sizeof(struct vmbus_channel_open_result)); - osd_WaitEventSet(msgInfo->WaitEvent); - break; - } - } - } - spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags); -} - -/* - * VmbusChannelOnGpadlCreated - GPADL created handler. - * - * This is invoked when we received a response to our gpadl create request. - * Find the matching request, copy the response and signal the requesting - * thread. - */ -static void VmbusChannelOnGpadlCreated(struct vmbus_channel_message_header *hdr) -{ - struct vmbus_channel_gpadl_created *gpadlCreated; - struct list_head *curr; - struct vmbus_channel_msginfo *msgInfo; - struct vmbus_channel_message_header *requestHeader; - struct vmbus_channel_gpadl_header *gpadlHeader; - unsigned long flags; - - gpadlCreated = (struct vmbus_channel_gpadl_created *)hdr; - DPRINT_DBG(VMBUS, "vmbus gpadl created result - %d", - gpadlCreated->CreationStatus); - - /* - * Find the establish msg, copy the result and signal/unblock the wait - * event - */ - spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags); - - list_for_each(curr, &gVmbusConnection.ChannelMsgList) { -/* FIXME: this should probably use list_entry() instead */ - msgInfo = (struct vmbus_channel_msginfo *)curr; - requestHeader = (struct vmbus_channel_message_header *)msgInfo->Msg; - - if (requestHeader->MessageType == ChannelMessageGpadlHeader) { - gpadlHeader = (struct vmbus_channel_gpadl_header *)requestHeader; - - if ((gpadlCreated->ChildRelId == - gpadlHeader->ChildRelId) && - (gpadlCreated->Gpadl == gpadlHeader->Gpadl)) { - memcpy(&msgInfo->Response.GpadlCreated, - gpadlCreated, - sizeof(struct vmbus_channel_gpadl_created)); - osd_WaitEventSet(msgInfo->WaitEvent); - break; - } - } - } - spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags); -} - -/* - * VmbusChannelOnGpadlTorndown - GPADL torndown handler. - * - * This is invoked when we received a response to our gpadl teardown request. - * Find the matching request, copy the response and signal the requesting - * thread. - */ -static void VmbusChannelOnGpadlTorndown( - struct vmbus_channel_message_header *hdr) -{ - struct vmbus_channel_gpadl_torndown *gpadlTorndown; - struct list_head *curr; - struct vmbus_channel_msginfo *msgInfo; - struct vmbus_channel_message_header *requestHeader; - struct vmbus_channel_gpadl_teardown *gpadlTeardown; - unsigned long flags; - - gpadlTorndown = (struct vmbus_channel_gpadl_torndown *)hdr; - - /* - * Find the open msg, copy the result and signal/unblock the wait event - */ - spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags); - - list_for_each(curr, &gVmbusConnection.ChannelMsgList) { -/* FIXME: this should probably use list_entry() instead */ - msgInfo = (struct vmbus_channel_msginfo *)curr; - requestHeader = (struct vmbus_channel_message_header *)msgInfo->Msg; - - if (requestHeader->MessageType == ChannelMessageGpadlTeardown) { - gpadlTeardown = (struct vmbus_channel_gpadl_teardown *)requestHeader; - - if (gpadlTorndown->Gpadl == gpadlTeardown->Gpadl) { - memcpy(&msgInfo->Response.GpadlTorndown, - gpadlTorndown, - sizeof(struct vmbus_channel_gpadl_torndown)); - osd_WaitEventSet(msgInfo->WaitEvent); - break; - } - } - } - spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags); -} - -/* - * VmbusChannelOnVersionResponse - Version response handler - * - * This is invoked when we received a response to our initiate contact request. - * Find the matching request, copy the response and signal the requesting - * thread. - */ -static void VmbusChannelOnVersionResponse( - struct vmbus_channel_message_header *hdr) -{ - struct list_head *curr; - struct vmbus_channel_msginfo *msgInfo; - struct vmbus_channel_message_header *requestHeader; - struct vmbus_channel_initiate_contact *initiate; - struct vmbus_channel_version_response *versionResponse; - unsigned long flags; - - versionResponse = (struct vmbus_channel_version_response *)hdr; - spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags); - - list_for_each(curr, &gVmbusConnection.ChannelMsgList) { -/* FIXME: this should probably use list_entry() instead */ - msgInfo = (struct vmbus_channel_msginfo *)curr; - requestHeader = (struct vmbus_channel_message_header *)msgInfo->Msg; - - if (requestHeader->MessageType == - ChannelMessageInitiateContact) { - initiate = (struct vmbus_channel_initiate_contact *)requestHeader; - memcpy(&msgInfo->Response.VersionResponse, - versionResponse, - sizeof(struct vmbus_channel_version_response)); - osd_WaitEventSet(msgInfo->WaitEvent); - } - } - spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags); -} - -/* Channel message dispatch table */ -static struct vmbus_channel_message_table_entry - gChannelMessageTable[ChannelMessageCount] = { - {ChannelMessageInvalid, NULL}, - {ChannelMessageOfferChannel, VmbusChannelOnOffer}, - {ChannelMessageRescindChannelOffer, VmbusChannelOnOfferRescind}, - {ChannelMessageRequestOffers, NULL}, - {ChannelMessageAllOffersDelivered, VmbusChannelOnOffersDelivered}, - {ChannelMessageOpenChannel, NULL}, - {ChannelMessageOpenChannelResult, VmbusChannelOnOpenResult}, - {ChannelMessageCloseChannel, NULL}, - {ChannelMessageGpadlHeader, NULL}, - {ChannelMessageGpadlBody, NULL}, - {ChannelMessageGpadlCreated, VmbusChannelOnGpadlCreated}, - {ChannelMessageGpadlTeardown, NULL}, - {ChannelMessageGpadlTorndown, VmbusChannelOnGpadlTorndown}, - {ChannelMessageRelIdReleased, NULL}, - {ChannelMessageInitiateContact, NULL}, - {ChannelMessageVersionResponse, VmbusChannelOnVersionResponse}, - {ChannelMessageUnload, NULL}, -}; - -/* - * VmbusOnChannelMessage - Handler for channel protocol messages. - * - * This is invoked in the vmbus worker thread context. - */ -void VmbusOnChannelMessage(void *Context) -{ - struct hv_message *msg = Context; - struct vmbus_channel_message_header *hdr; - int size; - - hdr = (struct vmbus_channel_message_header *)msg->u.Payload; - size = msg->Header.PayloadSize; - - DPRINT_DBG(VMBUS, "message type %d size %d", hdr->MessageType, size); - - if (hdr->MessageType >= ChannelMessageCount) { - DPRINT_ERR(VMBUS, - "Received invalid channel message type %d size %d", - hdr->MessageType, size); - print_hex_dump_bytes("", DUMP_PREFIX_NONE, - (unsigned char *)msg->u.Payload, size); - kfree(msg); - return; - } - - if (gChannelMessageTable[hdr->MessageType].messageHandler) - gChannelMessageTable[hdr->MessageType].messageHandler(hdr); - else - DPRINT_ERR(VMBUS, "Unhandled channel message type %d", - hdr->MessageType); - - /* Free the msg that was allocated in VmbusOnMsgDPC() */ - kfree(msg); -} - -/* - * VmbusChannelRequestOffers - Send a request to get all our pending offers. - */ -int VmbusChannelRequestOffers(void) -{ - struct vmbus_channel_message_header *msg; - struct vmbus_channel_msginfo *msgInfo; - int ret; - - msgInfo = kmalloc(sizeof(*msgInfo) + - sizeof(struct vmbus_channel_message_header), - GFP_KERNEL); - if (!msgInfo) - return -ENOMEM; - - msgInfo->WaitEvent = osd_WaitEventCreate(); - if (!msgInfo->WaitEvent) { - kfree(msgInfo); - return -ENOMEM; - } - - msg = (struct vmbus_channel_message_header *)msgInfo->Msg; - - msg->MessageType = ChannelMessageRequestOffers; - - /*SpinlockAcquire(gVmbusConnection.channelMsgLock); - INSERT_TAIL_LIST(&gVmbusConnection.channelMsgList, - &msgInfo->msgListEntry); - SpinlockRelease(gVmbusConnection.channelMsgLock);*/ - - ret = VmbusPostMessage(msg, - sizeof(struct vmbus_channel_message_header)); - if (ret != 0) { - DPRINT_ERR(VMBUS, "Unable to request offers - %d", ret); - - /*SpinlockAcquire(gVmbusConnection.channelMsgLock); - REMOVE_ENTRY_LIST(&msgInfo->msgListEntry); - SpinlockRelease(gVmbusConnection.channelMsgLock);*/ - - goto Cleanup; - } - /* osd_WaitEventWait(msgInfo->waitEvent); */ - - /*SpinlockAcquire(gVmbusConnection.channelMsgLock); - REMOVE_ENTRY_LIST(&msgInfo->msgListEntry); - SpinlockRelease(gVmbusConnection.channelMsgLock);*/ - - -Cleanup: - if (msgInfo) { - kfree(msgInfo->WaitEvent); - kfree(msgInfo); - } - - return ret; -} - -/* - * VmbusChannelReleaseUnattachedChannels - Release channels that are - * unattached/unconnected ie (no drivers associated) - */ -void VmbusChannelReleaseUnattachedChannels(void) -{ - struct vmbus_channel *channel, *pos; - struct vmbus_channel *start = NULL; - unsigned long flags; - - spin_lock_irqsave(&gVmbusConnection.channel_lock, flags); - - list_for_each_entry_safe(channel, pos, &gVmbusConnection.ChannelList, - ListEntry) { - if (channel == start) - break; - - if (!channel->DeviceObject->Driver) { - list_del(&channel->ListEntry); - DPRINT_INFO(VMBUS, - "Releasing unattached device object %p", - channel->DeviceObject); - - VmbusChildDeviceRemove(channel->DeviceObject); - FreeVmbusChannel(channel); - } else { - if (!start) - start = channel; - } - } - - spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags); -} - -/* eof */ diff --git a/drivers/staging/hv/ChannelMgmt.h b/drivers/staging/hv/ChannelMgmt.h deleted file mode 100644 index 6e2f84d..0000000 --- a/drivers/staging/hv/ChannelMgmt.h +++ b/dev/null @@ -1,320 +0,0 @@ -/* - * - * 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> - * - */ - - -#ifndef _CHANNEL_MGMT_H_ -#define _CHANNEL_MGMT_H_ - -#include <linux/list.h> -#include <linux/timer.h> -#include "RingBuffer.h" -#include "VmbusChannelInterface.h" -#include "VmbusPacketFormat.h" - -/* Version 1 messages */ -enum vmbus_channel_message_type { - ChannelMessageInvalid = 0, - ChannelMessageOfferChannel = 1, - ChannelMessageRescindChannelOffer = 2, - ChannelMessageRequestOffers = 3, - ChannelMessageAllOffersDelivered = 4, - ChannelMessageOpenChannel = 5, - ChannelMessageOpenChannelResult = 6, - ChannelMessageCloseChannel = 7, - ChannelMessageGpadlHeader = 8, - ChannelMessageGpadlBody = 9, - ChannelMessageGpadlCreated = 10, - ChannelMessageGpadlTeardown = 11, - ChannelMessageGpadlTorndown = 12, - ChannelMessageRelIdReleased = 13, - ChannelMessageInitiateContact = 14, - ChannelMessageVersionResponse = 15, - ChannelMessageUnload = 16, -#ifdef VMBUS_FEATURE_PARENT_OR_PEER_MEMORY_MAPPED_INTO_A_CHILD - ChannelMessageViewRangeAdd = 17, - ChannelMessageViewRangeRemove = 18, -#endif - ChannelMessageCount -}; - -struct vmbus_channel_message_header { - enum vmbus_channel_message_type MessageType; - u32 Padding; -} __attribute__((packed)); - -/* Query VMBus Version parameters */ -struct vmbus_channel_query_vmbus_version { - struct vmbus_channel_message_header Header; - u32 Version; -} __attribute__((packed)); - -/* VMBus Version Supported parameters */ -struct vmbus_channel_version_supported { - struct vmbus_channel_message_header Header; - bool VersionSupported; -} __attribute__((packed)); - -/* Offer Channel parameters */ -struct vmbus_channel_offer_channel { - struct vmbus_channel_message_header Header; - struct vmbus_channel_offer Offer; - u32 ChildRelId; - u8 MonitorId; - bool MonitorAllocated; -} __attribute__((packed)); - -/* Rescind Offer parameters */ -struct vmbus_channel_rescind_offer { - struct vmbus_channel_message_header Header; - u32 ChildRelId; -} __attribute__((packed)); - -/* - * Request Offer -- no parameters, SynIC message contains the partition ID - * Set Snoop -- no parameters, SynIC message contains the partition ID - * Clear Snoop -- no parameters, SynIC message contains the partition ID - * All Offers Delivered -- no parameters, SynIC message contains the partition - * ID - * Flush Client -- no parameters, SynIC message contains the partition ID - */ - -/* Open Channel parameters */ -struct vmbus_channel_open_channel { - struct vmbus_channel_message_header Header; - - /* Identifies the specific VMBus channel that is being opened. */ - u32 ChildRelId; - - /* ID making a particular open request at a channel offer unique. */ - u32 OpenId; - - /* GPADL for the channel's ring buffer. */ - u32 RingBufferGpadlHandle; - - /* GPADL for the channel's server context save area. */ - u32 ServerContextAreaGpadlHandle; - - /* - * The upstream ring buffer begins at offset zero in the memory - * described by RingBufferGpadlHandle. The downstream ring buffer - * follows it at this offset (in pages). - */ - u32 DownstreamRingBufferPageOffset; - - /* User-specific data to be passed along to the server endpoint. */ - unsigned char UserData[MAX_USER_DEFINED_BYTES]; -} __attribute__((packed)); - -/* Open Channel Result parameters */ -struct vmbus_channel_open_result { - struct vmbus_channel_message_header Header; - u32 ChildRelId; - u32 OpenId; - u32 Status; -} __attribute__((packed)); - -/* Close channel parameters; */ -struct vmbus_channel_close_channel { - struct vmbus_channel_message_header Header; - u32 ChildRelId; -} __attribute__((packed)); - -/* Channel Message GPADL */ -#define GPADL_TYPE_RING_BUFFER 1 -#define GPADL_TYPE_SERVER_SAVE_AREA 2 -#define GPADL_TYPE_TRANSACTION 8 - -/* - * The number of PFNs in a GPADL message is defined by the number of - * pages that would be spanned by ByteCount and ByteOffset. If the - * implied number of PFNs won't fit in this packet, there will be a - * follow-up packet that contains more. - */ -struct vmbus_channel_gpadl_header { - struct vmbus_channel_message_header Header; - u32 ChildRelId; - u32 Gpadl; - u16 RangeBufLen; - u16 RangeCount; - struct gpa_range Range[0]; -} __attribute__((packed)); - -/* This is the followup packet that contains more PFNs. */ -struct vmbus_channel_gpadl_body { - struct vmbus_channel_message_header Header; - u32 MessageNumber; - u32 Gpadl; - u64 Pfn[0]; -} __attribute__((packed)); - -struct vmbus_channel_gpadl_created { - struct vmbus_channel_message_header Header; - u32 ChildRelId; - u32 Gpadl; - u32 CreationStatus; -} __attribute__((packed)); - -struct vmbus_channel_gpadl_teardown { - struct vmbus_channel_message_header Header; - u32 ChildRelId; - u32 Gpadl; -} __attribute__((packed)); - -struct vmbus_channel_gpadl_torndown { - struct vmbus_channel_message_header Header; - u32 Gpadl; -} __attribute__((packed)); - -#ifdef VMBUS_FEATURE_PARENT_OR_PEER_MEMORY_MAPPED_INTO_A_CHILD -struct vmbus_channel_view_range_add { - struct vmbus_channel_message_header Header; - PHYSICAL_ADDRESS ViewRangeBase; - u64 ViewRangeLength; - u32 ChildRelId; -} __attribute__((packed)); - -struct vmbus_channel_view_range_remove { - struct vmbus_channel_message_header Header; - PHYSICAL_ADDRESS ViewRangeBase; - u32 ChildRelId; -} __attribute__((packed)); -#endif - -struct vmbus_channel_relid_released { - struct vmbus_channel_message_header Header; - u32 ChildRelId; -} __attribute__((packed)); - -struct vmbus_channel_initiate_contact { - struct vmbus_channel_message_header Header; - u32 VMBusVersionRequested; - u32 Padding2; - u64 InterruptPage; - u64 MonitorPage1; - u64 MonitorPage2; -} __attribute__((packed)); - -struct vmbus_channel_version_response { - struct vmbus_channel_message_header Header; - bool VersionSupported; -} __attribute__((packed)); - -enum vmbus_channel_state { - CHANNEL_OFFER_STATE, - CHANNEL_OPENING_STATE, - CHANNEL_OPEN_STATE, -}; - -struct vmbus_channel { - struct list_head ListEntry; - - struct hv_device *DeviceObject; - - struct timer_list poll_timer; /* SA-111 workaround */ - - enum vmbus_channel_state State; - - struct vmbus_channel_offer_channel OfferMsg; - /* - * These are based on the OfferMsg.MonitorId. - * Save it here for easy access. - */ - u8 MonitorGroup; - u8 MonitorBit; - - u32 RingBufferGpadlHandle; - - /* Allocated memory for ring buffer */ - void *RingBufferPages; - u32 RingBufferPageCount; - struct hv_ring_buffer_info Outbound; /* send to parent */ - struct hv_ring_buffer_info Inbound; /* receive from parent */ - spinlock_t inbound_lock; - struct workqueue_struct *ControlWQ; - - /* Channel callback are invoked in this workqueue context */ - /* HANDLE dataWorkQueue; */ - - void (*OnChannelCallback)(void *context); - void *ChannelCallbackContext; -}; - -struct vmbus_channel_debug_info { - u32 RelId; - enum vmbus_channel_state State; - struct hv_guid InterfaceType; - struct hv_guid InterfaceInstance; - u32 MonitorId; - u32 ServerMonitorPending; - u32 ServerMonitorLatency; - u32 ServerMonitorConnectionId; - u32 ClientMonitorPending; - u32 ClientMonitorLatency; - u32 ClientMonitorConnectionId; - - struct hv_ring_buffer_debug_info Inbound; - struct hv_ring_buffer_debug_info Outbound; -}; - -/* - * Represents each channel msg on the vmbus connection This is a - * variable-size data structure depending on the msg type itself - */ -struct vmbus_channel_msginfo { - /* Bookkeeping stuff */ - struct list_head MsgListEntry; - - /* So far, this is only used to handle gpadl body message */ - struct list_head SubMsgList; - - /* Synchronize the request/response if needed */ - struct osd_waitevent *WaitEvent; - - union { - struct vmbus_channel_version_supported VersionSupported; - struct vmbus_channel_open_result OpenResult; - struct vmbus_channel_gpadl_torndown GpadlTorndown; - struct vmbus_channel_gpadl_created GpadlCreated; - struct vmbus_channel_version_response VersionResponse; - } Response; - - u32 MessageSize; - /* - * The channel message that goes out on the "wire". - * It will contain at minimum the VMBUS_CHANNEL_MESSAGE_HEADER header - */ - unsigned char Msg[0]; -}; - - -struct vmbus_channel *AllocVmbusChannel(void); - -void FreeVmbusChannel(struct vmbus_channel *Channel); - -void VmbusOnChannelMessage(void *Context); - -int VmbusChannelRequestOffers(void); - -void VmbusChannelReleaseUnattachedChannels(void); - -#endif /* _CHANNEL_MGMT_H_ */ diff --git a/drivers/staging/hv/Connection.c b/drivers/staging/hv/Connection.c deleted file mode 100644 index b60b0c6..0000000 --- a/drivers/staging/hv/Connection.c +++ b/dev/null @@ -1,330 +0,0 @@ -/* - * - * 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/vmalloc.h> -#include "osd.h" -#include "logging.h" -#include "VmbusPrivate.h" - - -struct VMBUS_CONNECTION gVmbusConnection = { - .ConnectState = Disconnected, - .NextGpadlHandle = ATOMIC_INIT(0xE1E10), -}; - -/* - * VmbusConnect - Sends a connect request on the partition service connection - */ -int VmbusConnect(void) -{ - int ret = 0; - struct vmbus_channel_msginfo *msgInfo = NULL; - struct vmbus_channel_initiate_contact *msg; - unsigned long flags; - - /* Make sure we are not connecting or connected */ - if (gVmbusConnection.ConnectState != Disconnected) - return -1; - - /* Initialize the vmbus connection */ - gVmbusConnection.ConnectState = Connecting; - gVmbusConnection.WorkQueue = create_workqueue("hv_vmbus_con"); - if (!gVmbusConnection.WorkQueue) { - ret = -1; - goto Cleanup; - } - - INIT_LIST_HEAD(&gVmbusConnection.ChannelMsgList); - spin_lock_init(&gVmbusConnection.channelmsg_lock); - - INIT_LIST_HEAD(&gVmbusConnection.ChannelList); - spin_lock_init(&gVmbusConnection.channel_lock); - - /* - * Setup the vmbus event connection for channel interrupt - * abstraction stuff - */ - gVmbusConnection.InterruptPage = osd_PageAlloc(1); - if (gVmbusConnection.InterruptPage == NULL) { - ret = -1; - goto Cleanup; - } - - gVmbusConnection.RecvInterruptPage = gVmbusConnection.InterruptPage; - gVmbusConnection.SendInterruptPage = - (void *)((unsigned long)gVmbusConnection.InterruptPage + - (PAGE_SIZE >> 1)); - - /* - * Setup the monitor notification facility. The 1st page for - * parent->child and the 2nd page for child->parent - */ - gVmbusConnection.MonitorPages = osd_PageAlloc(2); - if (gVmbusConnection.MonitorPages == NULL) { - ret = -1; - goto Cleanup; - } - - msgInfo = kzalloc(sizeof(*msgInfo) + - sizeof(struct vmbus_channel_initiate_contact), - GFP_KERNEL); - if (msgInfo == NULL) { - ret = -ENOMEM; - goto Cleanup; - } - - msgInfo->WaitEvent = osd_WaitEventCreate(); - if (!msgInfo->WaitEvent) { - ret = -ENOMEM; - goto Cleanup; - } - - msg = (struct vmbus_channel_initiate_contact *)msgInfo->Msg; - - msg->Header.MessageType = ChannelMessageInitiateContact; - msg->VMBusVersionRequested = VMBUS_REVISION_NUMBER; - msg->InterruptPage = virt_to_phys(gVmbusConnection.InterruptPage); - msg->MonitorPage1 = virt_to_phys(gVmbusConnection.MonitorPages); - msg->MonitorPage2 = virt_to_phys( - (void *)((unsigned long)gVmbusConnection.MonitorPages + - PAGE_SIZE)); - - /* - * Add to list before we send the request since we may - * receive the response before returning from this routine - */ - spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags); - list_add_tail(&msgInfo->MsgListEntry, - &gVmbusConnection.ChannelMsgList); - - spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags); - - DPRINT_DBG(VMBUS, "Vmbus connection - interrupt pfn %llx, " - "monitor1 pfn %llx,, monitor2 pfn %llx", - msg->InterruptPage, msg->MonitorPage1, msg->MonitorPage2); - - DPRINT_DBG(VMBUS, "Sending channel initiate msg..."); - ret = VmbusPostMessage(msg, - sizeof(struct vmbus_channel_initiate_contact)); - if (ret != 0) { - list_del(&msgInfo->MsgListEntry); - goto Cleanup; - } - - /* Wait for the connection response */ - osd_WaitEventWait(msgInfo->WaitEvent); - - list_del(&msgInfo->MsgListEntry); - - /* Check if successful */ - if (msgInfo->Response.VersionResponse.VersionSupported) { - DPRINT_INFO(VMBUS, "Vmbus connected!!"); - gVmbusConnection.ConnectState = Connected; - - } else { - DPRINT_ERR(VMBUS, "Vmbus connection failed!!..." - "current version (%d) not supported", - VMBUS_REVISION_NUMBER); - ret = -1; - goto Cleanup; - } - - kfree(msgInfo->WaitEvent); - kfree(msgInfo); - return 0; - -Cleanup: - gVmbusConnection.ConnectState = Disconnected; - - if (gVmbusConnection.WorkQueue) - destroy_workqueue(gVmbusConnection.WorkQueue); - - if (gVmbusConnection.InterruptPage) { - osd_PageFree(gVmbusConnection.InterruptPage, 1); - gVmbusConnection.InterruptPage = NULL; - } - - if (gVmbusConnection.MonitorPages) { - osd_PageFree(gVmbusConnection.MonitorPages, 2); - gVmbusConnection.MonitorPages = NULL; - } - - if (msgInfo) { - kfree(msgInfo->WaitEvent); - kfree(msgInfo); - } - - return ret; -} - -/* - * VmbusDisconnect - Sends a disconnect request on the partition service connection - */ -int VmbusDisconnect(void) -{ - int ret = 0; - struct vmbus_channel_message_header *msg; - - /* Make sure we are connected */ - if (gVmbusConnection.ConnectState != Connected) - return -1; - - msg = kzalloc(sizeof(struct vmbus_channel_message_header), GFP_KERNEL); - if (!msg) - return -ENOMEM; - - msg->MessageType = ChannelMessageUnload; - - ret = VmbusPostMessage(msg, - sizeof(struct vmbus_channel_message_header)); - if (ret != 0) - goto Cleanup; - - osd_PageFree(gVmbusConnection.InterruptPage, 1); - - /* TODO: iterate thru the msg list and free up */ - destroy_workqueue(gVmbusConnection.WorkQueue); - - gVmbusConnection.ConnectState = Disconnected; - - DPRINT_INFO(VMBUS, "Vmbus disconnected!!"); - -Cleanup: - kfree(msg); - return ret; -} - -/* - * GetChannelFromRelId - Get the channel object given its child relative id (ie channel id) - */ -struct vmbus_channel *GetChannelFromRelId(u32 relId) -{ - struct vmbus_channel *channel; - struct vmbus_channel *foundChannel = NULL; - unsigned long flags; - - spin_lock_irqsave(&gVmbusConnection.channel_lock, flags); - list_for_each_entry(channel, &gVmbusConnection.ChannelList, ListEntry) { - if (channel->OfferMsg.ChildRelId == relId) { - foundChannel = channel; - break; - } - } - spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags); - - return foundChannel; -} - -/* - * VmbusProcessChannelEvent - Process a channel event notification - */ -static void VmbusProcessChannelEvent(void *context) -{ - struct vmbus_channel *channel; - u32 relId = (u32)(unsigned long)context; - - /* ASSERT(relId > 0); */ - - /* - * Find the channel based on this relid and invokes the - * channel callback to process the event - */ - channel = GetChannelFromRelId(relId); - - if (channel) { - VmbusChannelOnChannelEvent(channel); - /* - * WorkQueueQueueWorkItem(channel->dataWorkQueue, - * VmbusChannelOnChannelEvent, - * (void*)channel); - */ - } else { - DPRINT_ERR(VMBUS, "channel not found for relid - %d.", relId); - } -} - -/* - * VmbusOnEvents - Handler for events - */ -void VmbusOnEvents(void) -{ - int dword; - int maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5; - int bit; - int relid; - u32 *recvInterruptPage = gVmbusConnection.RecvInterruptPage; - - /* Check events */ - if (recvInterruptPage) { - for (dword = 0; dword < maxdword; dword++) { - if (recvInterruptPage[dword]) { - for (bit = 0; bit < 32; bit++) { - if (sync_test_and_clear_bit(bit, - (unsigned long *) - &recvInterruptPage[dword])) { - relid = (dword << 5) + bit; - DPRINT_DBG(VMBUS, "event detected for relid - %d", relid); - - if (relid == 0) { - /* special case - vmbus channel protocol msg */ - DPRINT_DBG(VMBUS, "invalid relid - %d", relid); - continue; - } else { - /* QueueWorkItem(VmbusProcessEvent, (void*)relid); */ - /* ret = WorkQueueQueueWorkItem(gVmbusConnection.workQueue, VmbusProcessChannelEvent, (void*)relid); */ - VmbusProcessChannelEvent((void *)(unsigned long)relid); - } - } - } - } - } - } - return; -} - -/* - * VmbusPostMessage - Send a msg on the vmbus's message connection - */ -int VmbusPostMessage(void *buffer, size_t bufferLen) -{ - union hv_connection_id connId; - - connId.Asu32 = 0; - connId.u.Id = VMBUS_MESSAGE_CONNECTION_ID; - return HvPostMessage(connId, 1, buffer, bufferLen); -} - -/* - * VmbusSetEvent - Send an event notification to the parent - */ -int VmbusSetEvent(u32 childRelId) -{ - /* Each u32 represents 32 channels */ - sync_set_bit(childRelId & 31, - (unsigned long *)gVmbusConnection.SendInterruptPage + - (childRelId >> 5)); - - return HvSignalEvent(); -} diff --git a/drivers/staging/hv/Hv.c b/drivers/staging/hv/Hv.c deleted file mode 100644 index d580270..0000000 --- a/drivers/staging/hv/Hv.c +++ b/dev/null @@ -1,499 +0,0 @@ -/* - * 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/vmalloc.h> -#include "osd.h" -#include "logging.h" -#include "VmbusPrivate.h" - -/* The one and only */ -struct hv_context gHvContext = { - .SynICInitialized = false, - .HypercallPage = NULL, - .SignalEventParam = NULL, - .SignalEventBuffer = NULL, -}; - -/* - * HvQueryHypervisorPresence - Query the cpuid for presense of windows hypervisor - */ -static int HvQueryHypervisorPresence(void) -{ - unsigned int eax; - unsigned int ebx; - unsigned int ecx; - unsigned int edx; - unsigned int op; - - eax = 0; - ebx = 0; - ecx = 0; - edx = 0; - op = HvCpuIdFunctionVersionAndFeatures; - cpuid(op, &eax, &ebx, &ecx, &edx); - - return ecx & HV_PRESENT_BIT; -} - -/* - * HvQueryHypervisorInfo - Get version info of the windows hypervisor - */ -static int HvQueryHypervisorInfo(void) -{ - unsigned int eax; - unsigned int ebx; - unsigned int ecx; - unsigned int edx; - unsigned int maxLeaf; - unsigned int op; - - /* - * Its assumed that this is called after confirming that Viridian - * is present. Query id and revision. - */ - eax = 0; - ebx = 0; - ecx = 0; - edx = 0; - op = HvCpuIdFunctionHvVendorAndMaxFunction; - cpuid(op, &eax, &ebx, &ecx, &edx); - - DPRINT_INFO(VMBUS, "Vendor ID: %c%c%c%c%c%c%c%c%c%c%c%c", - (ebx & 0xFF), - ((ebx >> 8) & 0xFF), - ((ebx >> 16) & 0xFF), - ((ebx >> 24) & 0xFF), - (ecx & 0xFF), - ((ecx >> 8) & 0xFF), - ((ecx >> 16) & 0xFF), - ((ecx >> 24) & 0xFF), - (edx & 0xFF), - ((edx >> 8) & 0xFF), - ((edx >> 16) & 0xFF), - ((edx >> 24) & 0xFF)); - - maxLeaf = eax; - eax = 0; - ebx = 0; - ecx = 0; - edx = 0; - op = HvCpuIdFunctionHvInterface; - cpuid(op, &eax, &ebx, &ecx, &edx); - - DPRINT_INFO(VMBUS, "Interface ID: %c%c%c%c", - (eax & 0xFF), - ((eax >> 8) & 0xFF), - ((eax >> 16) & 0xFF), - ((eax >> 24) & 0xFF)); - - if (maxLeaf >= HvCpuIdFunctionMsHvVersion) { - eax = 0; - ebx = 0; - ecx = 0; - edx = 0; - op = HvCpuIdFunctionMsHvVersion; - cpuid(op, &eax, &ebx, &ecx, &edx); - DPRINT_INFO(VMBUS, "OS Build:%d-%d.%d-%d-%d.%d",\ - eax, - ebx >> 16, - ebx & 0xFFFF, - ecx, - edx >> 24, - edx & 0xFFFFFF); - } - return maxLeaf; -} - -/* - * HvDoHypercall - Invoke the specified hypercall - */ -static u64 HvDoHypercall(u64 Control, void *Input, void *Output) -{ -#ifdef CONFIG_X86_64 - u64 hvStatus = 0; - u64 inputAddress = (Input) ? virt_to_phys(Input) : 0; - u64 outputAddress = (Output) ? virt_to_phys(Output) : 0; - volatile void *hypercallPage = gHvContext.HypercallPage; - - DPRINT_DBG(VMBUS, "Hypercall <control %llx input phys %llx virt %p " - "output phys %llx virt %p hypercall %p>", - Control, inputAddress, Input, - outputAddress, Output, hypercallPage); - - __asm__ __volatile__("mov %0, %%r8" : : "r" (outputAddress) : "r8"); - __asm__ __volatile__("call *%3" : "=a" (hvStatus) : - "c" (Control), "d" (inputAddress), - "m" (hypercallPage)); - - DPRINT_DBG(VMBUS, "Hypercall <return %llx>", hvStatus); - - return hvStatus; - -#else - - u32 controlHi = Control >> 32; - u32 controlLo = Control & 0xFFFFFFFF; - u32 hvStatusHi = 1; - u32 hvStatusLo = 1; - u64 inputAddress = (Input) ? virt_to_phys(Input) : 0; - u32 inputAddressHi = inputAddress >> 32; - u32 inputAddressLo = inputAddress & 0xFFFFFFFF; - u64 outputAddress = (Output) ? virt_to_phys(Output) : 0; - u32 outputAddressHi = outputAddress >> 32; - u32 outputAddressLo = outputAddress & 0xFFFFFFFF; - volatile void *hypercallPage = gHvContext.HypercallPage; - - DPRINT_DBG(VMBUS, "Hypercall <control %llx input %p output %p>", - Control, Input, Output); - - __asm__ __volatile__ ("call *%8" : "=d"(hvStatusHi), - "=a"(hvStatusLo) : "d" (controlHi), - "a" (controlLo), "b" (inputAddressHi), - "c" (inputAddressLo), "D"(outputAddressHi), - "S"(outputAddressLo), "m" (hypercallPage)); - - DPRINT_DBG(VMBUS, "Hypercall <return %llx>", - hvStatusLo | ((u64)hvStatusHi << 32)); - - return hvStatusLo | ((u64)hvStatusHi << 32); -#endif /* !x86_64 */ -} - -/* - * HvInit - Main initialization routine. - * - * This routine must be called before any other routines in here are called - */ -int HvInit(void) -{ - int ret = 0; - int maxLeaf; - union hv_x64_msr_hypercall_contents hypercallMsr; - void *virtAddr = NULL; - - memset(gHvContext.synICEventPage, 0, sizeof(void *) * MAX_NUM_CPUS); - memset(gHvContext.synICMessagePage, 0, sizeof(void *) * MAX_NUM_CPUS); - - if (!HvQueryHypervisorPresence()) { - DPRINT_ERR(VMBUS, "No Windows hypervisor detected!!"); - goto Cleanup; - } - - DPRINT_INFO(VMBUS, - "Windows hypervisor detected! Retrieving more info..."); - - maxLeaf = HvQueryHypervisorInfo(); - /* HvQueryHypervisorFeatures(maxLeaf); */ - - /* - * We only support running on top of Hyper-V - */ - rdmsrl(HV_X64_MSR_GUEST_OS_ID, gHvContext.GuestId); - - if (gHvContext.GuestId != 0) { - DPRINT_ERR(VMBUS, "Unknown guest id (0x%llx)!!", - gHvContext.GuestId); - goto Cleanup; - } - - /* Write our OS info */ - wrmsrl(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID); - gHvContext.GuestId = HV_LINUX_GUEST_ID; - - /* See if the hypercall page is already set */ - rdmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64); - - /* - * Allocate the hypercall page memory - * virtAddr = osd_PageAlloc(1); - */ - virtAddr = osd_VirtualAllocExec(PAGE_SIZE); - - if (!virtAddr) { - DPRINT_ERR(VMBUS, - "unable to allocate hypercall page!!"); - goto Cleanup; - } - - hypercallMsr.Enable = 1; - - hypercallMsr.GuestPhysicalAddress = vmalloc_to_pfn(virtAddr); - wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64); - - /* Confirm that hypercall page did get setup. */ - hypercallMsr.AsUINT64 = 0; - rdmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64); - - if (!hypercallMsr.Enable) { - DPRINT_ERR(VMBUS, "unable to set hypercall page!!"); - goto Cleanup; - } - - gHvContext.HypercallPage = virtAddr; - - DPRINT_INFO(VMBUS, "Hypercall page VA=%p, PA=0x%0llx", - gHvContext.HypercallPage, - (u64)hypercallMsr.GuestPhysicalAddress << PAGE_SHIFT); - - /* Setup the global signal event param for the signal event hypercall */ - gHvContext.SignalEventBuffer = - kmalloc(sizeof(struct hv_input_signal_event_buffer), - GFP_KERNEL); - if (!gHvContext.SignalEventBuffer) - goto Cleanup; - - gHvContext.SignalEventParam = - (struct hv_input_signal_event *) - (ALIGN_UP((unsigned long)gHvContext.SignalEventBuffer, - HV_HYPERCALL_PARAM_ALIGN)); - gHvContext.SignalEventParam->ConnectionId.Asu32 = 0; - gHvContext.SignalEventParam->ConnectionId.u.Id = - VMBUS_EVENT_CONNECTION_ID; - gHvContext.SignalEventParam->FlagNumber = 0; - gHvContext.SignalEventParam->RsvdZ = 0; - - return ret; - -Cleanup: - if (virtAddr) { - if (hypercallMsr.Enable) { - hypercallMsr.AsUINT64 = 0; - wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64); - } - - vfree(virtAddr); - } - ret = -1; - return ret; -} - -/* - * HvCleanup - Cleanup routine. - * - * This routine is called normally during driver unloading or exiting. - */ -void HvCleanup(void) -{ - union hv_x64_msr_hypercall_contents hypercallMsr; - - kfree(gHvContext.SignalEventBuffer); - gHvContext.SignalEventBuffer = NULL; - gHvContext.SignalEventParam = NULL; - - if (gHvContext.HypercallPage) { - hypercallMsr.AsUINT64 = 0; - wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64); - vfree(gHvContext.HypercallPage); - gHvContext.HypercallPage = NULL; - } -} - -/* - * HvPostMessage - Post a message using the hypervisor message IPC. - * - * This involves a hypercall. - */ -u16 HvPostMessage(union hv_connection_id connectionId, - enum hv_message_type messageType, - void *payload, size_t payloadSize) -{ - struct alignedInput { - u64 alignment8; - struct hv_input_post_message msg; - }; - - struct hv_input_post_message *alignedMsg; - u16 status; - unsigned long addr; - - if (payloadSize > HV_MESSAGE_PAYLOAD_BYTE_COUNT) - return -1; - - addr = (unsigned long)kmalloc(sizeof(struct alignedInput), GFP_ATOMIC); - if (!addr) - return -1; - - alignedMsg = (struct hv_input_post_message *) - (ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN)); - - alignedMsg->ConnectionId = connectionId; - alignedMsg->MessageType = messageType; - alignedMsg->PayloadSize = payloadSize; - memcpy((void *)alignedMsg->Payload, payload, payloadSize); - - status = HvDoHypercall(HvCallPostMessage, alignedMsg, NULL) & 0xFFFF; - - kfree((void *)addr); - - return status; -} - - -/* - * HvSignalEvent - Signal an event on the specified connection using the hypervisor event IPC. - * - * This involves a hypercall. - */ -u16 HvSignalEvent(void) -{ - u16 status; - - status = HvDoHypercall(HvCallSignalEvent, gHvContext.SignalEventParam, - NULL) & 0xFFFF; - return status; -} - -/* - * HvSynicInit - Initialize the Synthethic Interrupt Controller. - * - * If it is already initialized by another entity (ie x2v shim), we need to - * retrieve the initialized message and event pages. Otherwise, we create and - * initialize the message and event pages. - */ -void HvSynicInit(void *irqarg) -{ - u64 version; - union hv_synic_simp simp; - union hv_synic_siefp siefp; - union hv_synic_sint sharedSint; - union hv_synic_scontrol sctrl; - - u32 irqVector = *((u32 *)(irqarg)); - int cpu = smp_processor_id(); - - if (!gHvContext.HypercallPage) - return; - - /* Check the version */ - rdmsrl(HV_X64_MSR_SVERSION, version); - - DPRINT_INFO(VMBUS, "SynIC version: %llx", version); - - gHvContext.synICMessagePage[cpu] = (void *)get_zeroed_page(GFP_ATOMIC); - - if (gHvContext.synICMessagePage[cpu] == NULL) { - DPRINT_ERR(VMBUS, - "unable to allocate SYNIC message page!!"); - goto Cleanup; - } - - gHvContext.synICEventPage[cpu] = (void *)get_zeroed_page(GFP_ATOMIC); - - if (gHvContext.synICEventPage[cpu] == NULL) { - DPRINT_ERR(VMBUS, - "unable to allocate SYNIC event page!!"); - goto Cleanup; - } - - /* Setup the Synic's message page */ - rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64); - simp.SimpEnabled = 1; - simp.BaseSimpGpa = virt_to_phys(gHvContext.synICMessagePage[cpu]) - >> PAGE_SHIFT; - - DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx", simp.AsUINT64); - - wrmsrl(HV_X64_MSR_SIMP, simp.AsUINT64); - - /* Setup the Synic's event page */ - rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64); - siefp.SiefpEnabled = 1; - siefp.BaseSiefpGpa = virt_to_phys(gHvContext.synICEventPage[cpu]) - >> PAGE_SHIFT; - - DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx", siefp.AsUINT64); - - wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64); - - /* Setup the interception SINT. */ - /* wrmsrl((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX), */ - /* interceptionSint.AsUINT64); */ - - /* Setup the shared SINT. */ - rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64); - - sharedSint.AsUINT64 = 0; - sharedSint.Vector = irqVector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */ - sharedSint.Masked = false; - sharedSint.AutoEoi = true; - - DPRINT_DBG(VMBUS, "HV_X64_MSR_SINT1 msr set to: %llx", - sharedSint.AsUINT64); - - wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64); - - /* Enable the global synic bit */ - rdmsrl(HV_X64_MSR_SCONTROL, sctrl.AsUINT64); - sctrl.Enable = 1; - - wrmsrl(HV_X64_MSR_SCONTROL, sctrl.AsUINT64); - - gHvContext.SynICInitialized = true; - return; - -Cleanup: - if (gHvContext.synICEventPage[cpu]) - osd_PageFree(gHvContext.synICEventPage[cpu], 1); - - if (gHvContext.synICMessagePage[cpu]) - osd_PageFree(gHvContext.synICMessagePage[cpu], 1); - return; -} - -/* - * HvSynicCleanup - Cleanup routine for HvSynicInit(). - */ -void HvSynicCleanup(void *arg) -{ - union hv_synic_sint sharedSint; - union hv_synic_simp simp; - union hv_synic_siefp siefp; - int cpu = smp_processor_id(); - - if (!gHvContext.SynICInitialized) - return; - - rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64); - - sharedSint.Masked = 1; - - /* Need to correctly cleanup in the case of SMP!!! */ - /* Disable the interrupt */ - wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64); - - rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64); - simp.SimpEnabled = 0; - simp.BaseSimpGpa = 0; - - wrmsrl(HV_X64_MSR_SIMP, simp.AsUINT64); - - rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64); - siefp.SiefpEnabled = 0; - siefp.BaseSiefpGpa = 0; - - wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64); - - osd_PageFree(gHvContext.synICMessagePage[cpu], 1); - osd_PageFree(gHvContext.synICEventPage[cpu], 1); -} diff --git a/drivers/staging/hv/Hv.h b/drivers/staging/hv/Hv.h deleted file mode 100644 index 41f5ebb..0000000 --- a/drivers/staging/hv/Hv.h +++ b/dev/null @@ -1,140 +0,0 @@ -/* - * - * 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> - * - */ - - -#ifndef __HV_H__ -#define __HV_H__ - -#include "hv_api.h" - -enum { - VMBUS_MESSAGE_CONNECTION_ID = 1, - VMBUS_MESSAGE_PORT_ID = 1, - VMBUS_EVENT_CONNECTION_ID = 2, - VMBUS_EVENT_PORT_ID = 2, - VMBUS_MONITOR_CONNECTION_ID = 3, - VMBUS_MONITOR_PORT_ID = 3, - VMBUS_MESSAGE_SINT = 2, -}; - -/* #defines */ - -#define HV_PRESENT_BIT 0x80000000 - -#define HV_LINUX_GUEST_ID_LO 0x00000000 -#define HV_LINUX_GUEST_ID_HI 0xB16B00B5 -#define HV_LINUX_GUEST_ID (((u64)HV_LINUX_GUEST_ID_HI << 32) | \ - HV_LINUX_GUEST_ID_LO) - -#define HV_CPU_POWER_MANAGEMENT (1 << 0) -#define HV_RECOMMENDATIONS_MAX 4 - -#define HV_X64_MAX 5 -#define HV_CAPS_MAX 8 - - -#define HV_HYPERCALL_PARAM_ALIGN sizeof(u64) - - -/* Service definitions */ - -#define HV_SERVICE_PARENT_PORT (0) -#define HV_SERVICE_PARENT_CONNECTION (0) - -#define HV_SERVICE_CONNECT_RESPONSE_SUCCESS (0) -#define HV_SERVICE_CONNECT_RESPONSE_INVALID_PARAMETER (1) -#define HV_SERVICE_CONNECT_RESPONSE_UNKNOWN_SERVICE (2) -#define HV_SERVICE_CONNECT_RESPONSE_CONNECTION_REJECTED (3) - -#define HV_SERVICE_CONNECT_REQUEST_MESSAGE_ID (1) -#define HV_SERVICE_CONNECT_RESPONSE_MESSAGE_ID (2) -#define HV_SERVICE_DISCONNECT_REQUEST_MESSAGE_ID (3) -#define HV_SERVICE_DISCONNECT_RESPONSE_MESSAGE_ID (4) -#define HV_SERVICE_MAX_MESSAGE_ID (4) - -#define HV_SERVICE_PROTOCOL_VERSION (0x0010) -#define HV_CONNECT_PAYLOAD_BYTE_COUNT 64 - -/* #define VMBUS_REVISION_NUMBER 6 */ - -/* Our local vmbus's port and connection id. Anything >0 is fine */ -/* #define VMBUS_PORT_ID 11 */ - -/* 628180B8-308D-4c5e-B7DB-1BEB62E62EF4 */ -static const struct hv_guid VMBUS_SERVICE_ID = { - .data = { - 0xb8, 0x80, 0x81, 0x62, 0x8d, 0x30, 0x5e, 0x4c, - 0xb7, 0xdb, 0x1b, 0xeb, 0x62, 0xe6, 0x2e, 0xf4 - }, -}; - -#define MAX_NUM_CPUS 32 - - -struct hv_input_signal_event_buffer { - u64 Align8; - struct hv_input_signal_event Event; -}; - -struct hv_context { - /* We only support running on top of Hyper-V - * So at this point this really can only contain the Hyper-V ID - */ - u64 GuestId; - - void *HypercallPage; - - bool SynICInitialized; - - /* - * This is used as an input param to HvCallSignalEvent hypercall. The - * input param is immutable in our usage and must be dynamic mem (vs - * stack or global). */ - struct hv_input_signal_event_buffer *SignalEventBuffer; - /* 8-bytes aligned of the buffer above */ - struct hv_input_signal_event *SignalEventParam; - - void *synICMessagePage[MAX_NUM_CPUS]; - void *synICEventPage[MAX_NUM_CPUS]; -}; - -extern struct hv_context gHvContext; - - -/* Hv Interface */ - -extern int HvInit(void); - -extern void HvCleanup(void); - -extern u16 HvPostMessage(union hv_connection_id connectionId, - enum hv_message_type messageType, - void *payload, size_t payloadSize); - -extern u16 HvSignalEvent(void); - -extern void HvSynicInit(void *irqarg); - -extern void HvSynicCleanup(void *arg); - -#endif /* __HV_H__ */ diff --git a/drivers/staging/hv/Kconfig b/drivers/staging/hv/Kconfig index 022e621..1a67b19 100644 --- a/drivers/staging/hv/Kconfig +++ b/drivers/staging/hv/Kconfig @@ -1,6 +1,6 @@ config HYPERV tristate "Microsoft Hyper-V client drivers" - depends on X86 && !XEN && m + depends on X86 && !XEN && ACPI && PCI && m default n help Select this option to run Linux as a Hyper-V client operating @@ -17,7 +17,7 @@ config HYPERV_STORAGE config HYPERV_BLOCK tristate "Microsoft Hyper-V virtual block driver" - depends on BLOCK && SCSI + depends on BLOCK && SCSI && (LBDAF || 64BIT) default HYPERV help Select this option to enable the Hyper-V virtual block driver. @@ -31,8 +31,16 @@ config HYPERV_NET config HYPERV_UTILS tristate "Microsoft Hyper-V Utilities driver" + depends on CONNECTOR && NLS default HYPERV help Select this option to enable the Hyper-V Utilities. +config HYPERV_MOUSE + tristate "Microsoft Hyper-V mouse driver" + depends on HID + default HYPERV + help + Select this option to enable the Hyper-V mouse driver. + endif diff --git a/drivers/staging/hv/Makefile b/drivers/staging/hv/Makefile index 3ded0bc..fb94c40 100644 --- a/drivers/staging/hv/Makefile +++ b/drivers/staging/hv/Makefile @@ -3,11 +3,14 @@ obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o obj-$(CONFIG_HYPERV_BLOCK) += hv_blkvsc.o obj-$(CONFIG_HYPERV_NET) += hv_netvsc.o obj-$(CONFIG_HYPERV_UTILS) += hv_utils.o +obj-$(CONFIG_HYPERV_MOUSE) += hv_mouse.o -hv_vmbus-objs := vmbus_drv.o osd.o \ - Vmbus.o Hv.o Connection.o Channel.o \ - ChannelMgmt.o ChannelInterface.o RingBuffer.o -hv_storvsc-objs := storvsc_drv.o StorVsc.o -hv_blkvsc-objs := blkvsc_drv.o BlkVsc.o -hv_netvsc-objs := netvsc_drv.o NetVsc.o RndisFilter.o -hv_utils-objs := hyperv_utils.o ext_utils.o +EXTRA_CFLAGS += -I$(src)/include + +hv_vmbus-y := vmbus_drv.o \ + hv.o connection.o channel.o \ + channel_mgmt.o ring_buffer.o +hv_storvsc-y := storvsc_drv.o storvsc.o +hv_blkvsc-y := blkvsc_drv.o storvsc.o +hv_netvsc-y := netvsc_drv.o netvsc.o rndis_filter.o +hv_utils-y := hv_util.o hv_kvp.o diff --git a/drivers/staging/hv/NetVsc.c b/drivers/staging/hv/NetVsc.c deleted file mode 100644 index b3e3181..0000000 --- a/drivers/staging/hv/NetVsc.c +++ b/dev/null @@ -1,1328 +0,0 @@ -/* - * 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/delay.h> -#include <linux/io.h> -#include <linux/slab.h> -#include "osd.h" -#include "logging.h" -#include "NetVsc.h" -#include "RndisFilter.h" - - -/* Globals */ -static const char *gDriverName = "netvsc"; - -/* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */ -static const struct hv_guid gNetVscDeviceType = { - .data = { - 0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, - 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E - } -}; - -static int NetVscOnDeviceAdd(struct hv_device *Device, void *AdditionalInfo); - -static int NetVscOnDeviceRemove(struct hv_device *Device); - -static void NetVscOnCleanup(struct hv_driver *Driver); - -static void NetVscOnChannelCallback(void *context); - -static int NetVscInitializeSendBufferWithNetVsp(struct hv_device *Device); - -static int NetVscInitializeReceiveBufferWithNetVsp(struct hv_device *Device); - -static int NetVscDestroySendBuffer(struct netvsc_device *NetDevice); - -static int NetVscDestroyReceiveBuffer(struct netvsc_device *NetDevice); - -static int NetVscConnectToVsp(struct hv_device *Device); - -static void NetVscOnSendCompletion(struct hv_device *Device, - struct vmpacket_descriptor *Packet); - -static int NetVscOnSend(struct hv_device *Device, - struct hv_netvsc_packet *Packet); - -static void NetVscOnReceive(struct hv_device *Device, - struct vmpacket_descriptor *Packet); - -static void NetVscOnReceiveCompletion(void *Context); - -static void NetVscSendReceiveCompletion(struct hv_device *Device, - u64 TransactionId); - - -static struct netvsc_device *AllocNetDevice(struct hv_device *Device) -{ - struct netvsc_device *netDevice; - - netDevice = kzalloc(sizeof(struct netvsc_device), GFP_KERNEL); - if (!netDevice) - return NULL; - - /* Set to 2 to allow both inbound and outbound traffic */ - atomic_cmpxchg(&netDevice->RefCount, 0, 2); - - netDevice->Device = Device; - Device->Extension = netDevice; - - return netDevice; -} - -static void FreeNetDevice(struct netvsc_device *Device) -{ - WARN_ON(atomic_read(&Device->RefCount) == 0); - Device->Device->Extension = NULL; - kfree(Device); -} - - -/* Get the net device object iff exists and its refcount > 1 */ -static struct netvsc_device *GetOutboundNetDevice(struct hv_device *Device) -{ - struct netvsc_device *netDevice; - - netDevice = Device->Extension; - if (netDevice && atomic_read(&netDevice->RefCount) > 1) - atomic_inc(&netDevice->RefCount); - else - netDevice = NULL; - - return netDevice; -} - -/* Get the net device object iff exists and its refcount > 0 */ -static struct netvsc_device *GetInboundNetDevice(struct hv_device *Device) -{ - struct netvsc_device *netDevice; - - netDevice = Device->Extension; - if (netDevice && atomic_read(&netDevice->RefCount)) - atomic_inc(&netDevice->RefCount); - else - netDevice = NULL; - - return netDevice; -} - -static void PutNetDevice(struct hv_device *Device) -{ - struct netvsc_device *netDevice; - - netDevice = Device->Extension; - /* ASSERT(netDevice); */ - - atomic_dec(&netDevice->RefCount); -} - -static struct netvsc_device *ReleaseOutboundNetDevice(struct hv_device *Device) -{ - struct netvsc_device *netDevice; - - netDevice = Device->Extension; - if (netDevice == NULL) - return NULL; - - /* Busy wait until the ref drop to 2, then set it to 1 */ - while (atomic_cmpxchg(&netDevice->RefCount, 2, 1) != 2) - udelay(100); - - return netDevice; -} - -static struct netvsc_device *ReleaseInboundNetDevice(struct hv_device *Device) -{ - struct netvsc_device *netDevice; - - netDevice = Device->Extension; - if (netDevice == NULL) - return NULL; - - /* Busy wait until the ref drop to 1, then set it to 0 */ - while (atomic_cmpxchg(&netDevice->RefCount, 1, 0) != 1) - udelay(100); - - Device->Extension = NULL; - return netDevice; -} - -/* - * NetVscInitialize - Main entry point - */ -int NetVscInitialize(struct hv_driver *drv) -{ - struct netvsc_driver *driver = (struct netvsc_driver *)drv; - - DPRINT_DBG(NETVSC, "sizeof(struct hv_netvsc_packet)=%zd, " - "sizeof(struct nvsp_message)=%zd, " - "sizeof(struct vmtransfer_page_packet_header)=%zd", - sizeof(struct hv_netvsc_packet), - sizeof(struct nvsp_message), - sizeof(struct vmtransfer_page_packet_header)); - - /* Make sure we are at least 2 pages since 1 page is used for control */ - /* ASSERT(driver->RingBufferSize >= (PAGE_SIZE << 1)); */ - - drv->name = gDriverName; - memcpy(&drv->deviceType, &gNetVscDeviceType, sizeof(struct hv_guid)); - - /* Make sure it is set by the caller */ - /* FIXME: These probably should still be tested in some way */ - /* ASSERT(driver->OnReceiveCallback); */ - /* ASSERT(driver->OnLinkStatusChanged); */ - - /* Setup the dispatch table */ - driver->Base.OnDeviceAdd = NetVscOnDeviceAdd; - driver->Base.OnDeviceRemove = NetVscOnDeviceRemove; - driver->Base.OnCleanup = NetVscOnCleanup; - - driver->OnSend = NetVscOnSend; - - RndisFilterInit(driver); - return 0; -} - -static int NetVscInitializeReceiveBufferWithNetVsp(struct hv_device *Device) -{ - int ret = 0; - struct netvsc_device *netDevice; - struct nvsp_message *initPacket; - - netDevice = GetOutboundNetDevice(Device); - if (!netDevice) { - DPRINT_ERR(NETVSC, "unable to get net device..." - "device being destroyed?"); - return -1; - } - /* ASSERT(netDevice->ReceiveBufferSize > 0); */ - /* page-size grandularity */ - /* ASSERT((netDevice->ReceiveBufferSize & (PAGE_SIZE - 1)) == 0); */ - - netDevice->ReceiveBuffer = - osd_PageAlloc(netDevice->ReceiveBufferSize >> PAGE_SHIFT); - if (!netDevice->ReceiveBuffer) { - DPRINT_ERR(NETVSC, - "unable to allocate receive buffer of size %d", - netDevice->ReceiveBufferSize); - ret = -1; - goto Cleanup; - } - /* page-aligned buffer */ - /* ASSERT(((unsigned long)netDevice->ReceiveBuffer & (PAGE_SIZE - 1)) == */ - /* 0); */ - - DPRINT_INFO(NETVSC, "Establishing receive buffer's GPADL..."); - - /* - * Establish the gpadl handle for this buffer on this - * channel. Note: This call uses the vmbus connection rather - * than the channel to establish the gpadl handle. - */ - ret = Device->Driver->VmbusChannelInterface.EstablishGpadl(Device, - netDevice->ReceiveBuffer, - netDevice->ReceiveBufferSize, - &netDevice->ReceiveBufferGpadlHandle); - if (ret != 0) { - DPRINT_ERR(NETVSC, - "unable to establish receive buffer's gpadl"); - goto Cleanup; - } - - /* osd_WaitEventWait(ext->ChannelInitEvent); */ - - /* Notify the NetVsp of the gpadl handle */ - DPRINT_INFO(NETVSC, "Sending NvspMessage1TypeSendReceiveBuffer..."); - - initPacket = &netDevice->ChannelInitPacket; - - memset(initPacket, 0, sizeof(struct nvsp_message)); - - initPacket->Header.MessageType = NvspMessage1TypeSendReceiveBuffer; - initPacket->Messages.Version1Messages.SendReceiveBuffer.GpadlHandle = netDevice->ReceiveBufferGpadlHandle; - initPacket->Messages.Version1Messages.SendReceiveBuffer.Id = NETVSC_RECEIVE_BUFFER_ID; - - /* Send the gpadl notification request */ - ret = Device->Driver->VmbusChannelInterface.SendPacket(Device, - initPacket, - sizeof(struct nvsp_message), - (unsigned long)initPacket, - VmbusPacketTypeDataInBand, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret != 0) { - DPRINT_ERR(NETVSC, - "unable to send receive buffer's gpadl to netvsp"); - goto Cleanup; - } - - osd_WaitEventWait(netDevice->ChannelInitEvent); - - /* Check the response */ - if (initPacket->Messages.Version1Messages.SendReceiveBufferComplete.Status != NvspStatusSuccess) { - DPRINT_ERR(NETVSC, "Unable to complete receive buffer " - "initialzation with NetVsp - status %d", - initPacket->Messages.Version1Messages.SendReceiveBufferComplete.Status); - ret = -1; - goto Cleanup; - } - - /* Parse the response */ - /* ASSERT(netDevice->ReceiveSectionCount == 0); */ - /* ASSERT(netDevice->ReceiveSections == NULL); */ - - netDevice->ReceiveSectionCount = initPacket->Messages.Version1Messages.SendReceiveBufferComplete.NumSections; - - netDevice->ReceiveSections = kmalloc(netDevice->ReceiveSectionCount * sizeof(struct nvsp_1_receive_buffer_section), GFP_KERNEL); - if (netDevice->ReceiveSections == NULL) { - ret = -1; - goto Cleanup; - } - - memcpy(netDevice->ReceiveSections, - initPacket->Messages.Version1Messages.SendReceiveBufferComplete.Sections, - netDevice->ReceiveSectionCount * sizeof(struct nvsp_1_receive_buffer_section)); - - DPRINT_INFO(NETVSC, "Receive sections info (count %d, offset %d, " - "endoffset %d, suballoc size %d, num suballocs %d)", - netDevice->ReceiveSectionCount, - netDevice->ReceiveSections[0].Offset, - netDevice->ReceiveSections[0].EndOffset, - netDevice->ReceiveSections[0].SubAllocationSize, - netDevice->ReceiveSections[0].NumSubAllocations); - - /* - * For 1st release, there should only be 1 section that represents the - * entire receive buffer - */ - if (netDevice->ReceiveSectionCount != 1 || - netDevice->ReceiveSections->Offset != 0) { - ret = -1; - goto Cleanup; - } - - goto Exit; - -Cleanup: - NetVscDestroyReceiveBuffer(netDevice); - -Exit: - PutNetDevice(Device); - return ret; -} - -static int NetVscInitializeSendBufferWithNetVsp(struct hv_device *Device) -{ - int ret = 0; - struct netvsc_device *netDevice; - struct nvsp_message *initPacket; - - netDevice = GetOutboundNetDevice(Device); - if (!netDevice) { - DPRINT_ERR(NETVSC, "unable to get net device..." - "device being destroyed?"); - return -1; - } - if (netDevice->SendBufferSize <= 0) { - ret = -EINVAL; - goto Cleanup; - } - - /* page-size grandularity */ - /* ASSERT((netDevice->SendBufferSize & (PAGE_SIZE - 1)) == 0); */ - - netDevice->SendBuffer = - osd_PageAlloc(netDevice->SendBufferSize >> PAGE_SHIFT); - if (!netDevice->SendBuffer) { - DPRINT_ERR(NETVSC, "unable to allocate send buffer of size %d", - netDevice->SendBufferSize); - ret = -1; - goto Cleanup; - } - /* page-aligned buffer */ - /* ASSERT(((unsigned long)netDevice->SendBuffer & (PAGE_SIZE - 1)) == 0); */ - - DPRINT_INFO(NETVSC, "Establishing send buffer's GPADL..."); - - /* - * Establish the gpadl handle for this buffer on this - * channel. Note: This call uses the vmbus connection rather - * than the channel to establish the gpadl handle. - */ - ret = Device->Driver->VmbusChannelInterface.EstablishGpadl(Device, - netDevice->SendBuffer, - netDevice->SendBufferSize, - &netDevice->SendBufferGpadlHandle); - if (ret != 0) { - DPRINT_ERR(NETVSC, "unable to establish send buffer's gpadl"); - goto Cleanup; - } - - /* osd_WaitEventWait(ext->ChannelInitEvent); */ - - /* Notify the NetVsp of the gpadl handle */ - DPRINT_INFO(NETVSC, "Sending NvspMessage1TypeSendSendBuffer..."); - - initPacket = &netDevice->ChannelInitPacket; - - memset(initPacket, 0, sizeof(struct nvsp_message)); - - initPacket->Header.MessageType = NvspMessage1TypeSendSendBuffer; - initPacket->Messages.Version1Messages.SendReceiveBuffer.GpadlHandle = netDevice->SendBufferGpadlHandle; - initPacket->Messages.Version1Messages.SendReceiveBuffer.Id = NETVSC_SEND_BUFFER_ID; - - /* Send the gpadl notification request */ - ret = Device->Driver->VmbusChannelInterface.SendPacket(Device, - initPacket, sizeof(struct nvsp_message), - (unsigned long)initPacket, - VmbusPacketTypeDataInBand, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret != 0) { - DPRINT_ERR(NETVSC, - "unable to send receive buffer's gpadl to netvsp"); - goto Cleanup; - } - - osd_WaitEventWait(netDevice->ChannelInitEvent); - - /* Check the response */ - if (initPacket->Messages.Version1Messages.SendSendBufferComplete.Status != NvspStatusSuccess) { - DPRINT_ERR(NETVSC, "Unable to complete send buffer " - "initialzation with NetVsp - status %d", - initPacket->Messages.Version1Messages.SendSendBufferComplete.Status); - ret = -1; - goto Cleanup; - } - - netDevice->SendSectionSize = initPacket->Messages.Version1Messages.SendSendBufferComplete.SectionSize; - - goto Exit; - -Cleanup: - NetVscDestroySendBuffer(netDevice); - -Exit: - PutNetDevice(Device); - return ret; -} - -static int NetVscDestroyReceiveBuffer(struct netvsc_device *NetDevice) -{ - struct nvsp_message *revokePacket; - int ret = 0; - - /* - * If we got a section count, it means we received a - * SendReceiveBufferComplete msg (ie sent - * NvspMessage1TypeSendReceiveBuffer msg) therefore, we need - * to send a revoke msg here - */ - if (NetDevice->ReceiveSectionCount) { - DPRINT_INFO(NETVSC, - "Sending NvspMessage1TypeRevokeReceiveBuffer..."); - - /* Send the revoke receive buffer */ - revokePacket = &NetDevice->RevokePacket; - memset(revokePacket, 0, sizeof(struct nvsp_message)); - - revokePacket->Header.MessageType = NvspMessage1TypeRevokeReceiveBuffer; - revokePacket->Messages.Version1Messages.RevokeReceiveBuffer.Id = NETVSC_RECEIVE_BUFFER_ID; - - ret = NetDevice->Device->Driver->VmbusChannelInterface.SendPacket( - NetDevice->Device, - revokePacket, - sizeof(struct nvsp_message), - (unsigned long)revokePacket, - VmbusPacketTypeDataInBand, 0); - /* - * If we failed here, we might as well return and - * have a leak rather than continue and a bugchk - */ - if (ret != 0) { - DPRINT_ERR(NETVSC, "unable to send revoke receive " - "buffer to netvsp"); - return -1; - } - } - - /* Teardown the gpadl on the vsp end */ - if (NetDevice->ReceiveBufferGpadlHandle) { - DPRINT_INFO(NETVSC, "Tearing down receive buffer's GPADL..."); - - ret = NetDevice->Device->Driver->VmbusChannelInterface.TeardownGpadl( - NetDevice->Device, - NetDevice->ReceiveBufferGpadlHandle); - - /* If we failed here, we might as well return and have a leak rather than continue and a bugchk */ - if (ret != 0) { - DPRINT_ERR(NETVSC, - "unable to teardown receive buffer's gpadl"); - return -1; - } - NetDevice->ReceiveBufferGpadlHandle = 0; - } - - if (NetDevice->ReceiveBuffer) { - DPRINT_INFO(NETVSC, "Freeing up receive buffer..."); - - /* Free up the receive buffer */ - osd_PageFree(NetDevice->ReceiveBuffer, - NetDevice->ReceiveBufferSize >> PAGE_SHIFT); - NetDevice->ReceiveBuffer = NULL; - } - - if (NetDevice->ReceiveSections) { - NetDevice->ReceiveSectionCount = 0; - kfree(NetDevice->ReceiveSections); - NetDevice->ReceiveSections = NULL; - } - - return ret; -} - -static int NetVscDestroySendBuffer(struct netvsc_device *NetDevice) -{ - struct nvsp_message *revokePacket; - int ret = 0; - - /* - * If we got a section count, it means we received a - * SendReceiveBufferComplete msg (ie sent - * NvspMessage1TypeSendReceiveBuffer msg) therefore, we need - * to send a revoke msg here - */ - if (NetDevice->SendSectionSize) { - DPRINT_INFO(NETVSC, - "Sending NvspMessage1TypeRevokeSendBuffer..."); - - /* Send the revoke send buffer */ - revokePacket = &NetDevice->RevokePacket; - memset(revokePacket, 0, sizeof(struct nvsp_message)); - - revokePacket->Header.MessageType = NvspMessage1TypeRevokeSendBuffer; - revokePacket->Messages.Version1Messages.RevokeSendBuffer.Id = NETVSC_SEND_BUFFER_ID; - - ret = NetDevice->Device->Driver->VmbusChannelInterface.SendPacket(NetDevice->Device, - revokePacket, - sizeof(struct nvsp_message), - (unsigned long)revokePacket, - VmbusPacketTypeDataInBand, 0); - /* - * If we failed here, we might as well return and have a leak - * rather than continue and a bugchk - */ - if (ret != 0) { - DPRINT_ERR(NETVSC, "unable to send revoke send buffer " - "to netvsp"); - return -1; - } - } - - /* Teardown the gpadl on the vsp end */ - if (NetDevice->SendBufferGpadlHandle) { - DPRINT_INFO(NETVSC, "Tearing down send buffer's GPADL..."); - - ret = NetDevice->Device->Driver->VmbusChannelInterface.TeardownGpadl(NetDevice->Device, NetDevice->SendBufferGpadlHandle); - - /* - * If we failed here, we might as well return and have a leak - * rather than continue and a bugchk - */ - if (ret != 0) { - DPRINT_ERR(NETVSC, "unable to teardown send buffer's " - "gpadl"); - return -1; - } - NetDevice->SendBufferGpadlHandle = 0; - } - - if (NetDevice->SendBuffer) { - DPRINT_INFO(NETVSC, "Freeing up send buffer..."); - - /* Free up the receive buffer */ - osd_PageFree(NetDevice->SendBuffer, - NetDevice->SendBufferSize >> PAGE_SHIFT); - NetDevice->SendBuffer = NULL; - } - - return ret; -} - - -static int NetVscConnectToVsp(struct hv_device *Device) -{ - int ret; - struct netvsc_device *netDevice; - struct nvsp_message *initPacket; - int ndisVersion; - - netDevice = GetOutboundNetDevice(Device); - if (!netDevice) { - DPRINT_ERR(NETVSC, "unable to get net device..." - "device being destroyed?"); - return -1; - } - - initPacket = &netDevice->ChannelInitPacket; - - memset(initPacket, 0, sizeof(struct nvsp_message)); - initPacket->Header.MessageType = NvspMessageTypeInit; - initPacket->Messages.InitMessages.Init.MinProtocolVersion = NVSP_MIN_PROTOCOL_VERSION; - initPacket->Messages.InitMessages.Init.MaxProtocolVersion = NVSP_MAX_PROTOCOL_VERSION; - - DPRINT_INFO(NETVSC, "Sending NvspMessageTypeInit..."); - - /* Send the init request */ - ret = Device->Driver->VmbusChannelInterface.SendPacket(Device, - initPacket, - sizeof(struct nvsp_message), - (unsigned long)initPacket, - VmbusPacketTypeDataInBand, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - - if (ret != 0) { - DPRINT_ERR(NETVSC, "unable to send NvspMessageTypeInit"); - goto Cleanup; - } - - osd_WaitEventWait(netDevice->ChannelInitEvent); - - /* Now, check the response */ - /* ASSERT(initPacket->Messages.InitMessages.InitComplete.MaximumMdlChainLength <= MAX_MULTIPAGE_BUFFER_COUNT); */ - DPRINT_INFO(NETVSC, "NvspMessageTypeInit status(%d) max mdl chain (%d)", - initPacket->Messages.InitMessages.InitComplete.Status, - initPacket->Messages.InitMessages.InitComplete.MaximumMdlChainLength); - - if (initPacket->Messages.InitMessages.InitComplete.Status != - NvspStatusSuccess) { - DPRINT_ERR(NETVSC, - "unable to initialize with netvsp (status 0x%x)", - initPacket->Messages.InitMessages.InitComplete.Status); - ret = -1; - goto Cleanup; - } - - if (initPacket->Messages.InitMessages.InitComplete.NegotiatedProtocolVersion != NVSP_PROTOCOL_VERSION_1) { - DPRINT_ERR(NETVSC, "unable to initialize with netvsp " - "(version expected 1 got %d)", - initPacket->Messages.InitMessages.InitComplete.NegotiatedProtocolVersion); - ret = -1; - goto Cleanup; - } - DPRINT_INFO(NETVSC, "Sending NvspMessage1TypeSendNdisVersion..."); - - /* Send the ndis version */ - memset(initPacket, 0, sizeof(struct nvsp_message)); - - ndisVersion = 0x00050000; - - initPacket->Header.MessageType = NvspMessage1TypeSendNdisVersion; - initPacket->Messages.Version1Messages.SendNdisVersion.NdisMajorVersion = - (ndisVersion & 0xFFFF0000) >> 16; - initPacket->Messages.Version1Messages.SendNdisVersion.NdisMinorVersion = - ndisVersion & 0xFFFF; - - /* Send the init request */ - ret = Device->Driver->VmbusChannelInterface.SendPacket(Device, - initPacket, - sizeof(struct nvsp_message), - (unsigned long)initPacket, - VmbusPacketTypeDataInBand, 0); - if (ret != 0) { - DPRINT_ERR(NETVSC, - "unable to send NvspMessage1TypeSendNdisVersion"); - ret = -1; - goto Cleanup; - } - /* - * BUGBUG - We have to wait for the above msg since the - * netvsp uses KMCL which acknowledges packet (completion - * packet) since our Vmbus always set the - * VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED flag - */ - /* osd_WaitEventWait(NetVscChannel->ChannelInitEvent); */ - - /* Post the big receive buffer to NetVSP */ - ret = NetVscInitializeReceiveBufferWithNetVsp(Device); - if (ret == 0) - ret = NetVscInitializeSendBufferWithNetVsp(Device); - -Cleanup: - PutNetDevice(Device); - return ret; -} - -static void NetVscDisconnectFromVsp(struct netvsc_device *NetDevice) -{ - NetVscDestroyReceiveBuffer(NetDevice); - NetVscDestroySendBuffer(NetDevice); -} - -/* - * NetVscOnDeviceAdd - Callback when the device belonging to this driver is added - */ -static int NetVscOnDeviceAdd(struct hv_device *Device, void *AdditionalInfo) -{ - int ret = 0; - int i; - struct netvsc_device *netDevice; - struct hv_netvsc_packet *packet, *pos; - struct netvsc_driver *netDriver = - (struct netvsc_driver *)Device->Driver; - - netDevice = AllocNetDevice(Device); - if (!netDevice) { - ret = -1; - goto Cleanup; - } - - DPRINT_DBG(NETVSC, "netvsc channel object allocated - %p", netDevice); - - /* Initialize the NetVSC channel extension */ - netDevice->ReceiveBufferSize = NETVSC_RECEIVE_BUFFER_SIZE; - spin_lock_init(&netDevice->receive_packet_list_lock); - - netDevice->SendBufferSize = NETVSC_SEND_BUFFER_SIZE; - - INIT_LIST_HEAD(&netDevice->ReceivePacketList); - - for (i = 0; i < NETVSC_RECEIVE_PACKETLIST_COUNT; i++) { - packet = kzalloc(sizeof(struct hv_netvsc_packet) + - (NETVSC_RECEIVE_SG_COUNT * - sizeof(struct hv_page_buffer)), GFP_KERNEL); - if (!packet) { - DPRINT_DBG(NETVSC, "unable to allocate netvsc pkts " - "for receive pool (wanted %d got %d)", - NETVSC_RECEIVE_PACKETLIST_COUNT, i); - break; - } - list_add_tail(&packet->ListEntry, - &netDevice->ReceivePacketList); - } - netDevice->ChannelInitEvent = osd_WaitEventCreate(); - if (!netDevice->ChannelInitEvent) { - ret = -ENOMEM; - goto Cleanup; - } - - /* Open the channel */ - ret = Device->Driver->VmbusChannelInterface.Open(Device, - netDriver->RingBufferSize, - netDriver->RingBufferSize, - NULL, 0, - NetVscOnChannelCallback, - Device); - - if (ret != 0) { - DPRINT_ERR(NETVSC, "unable to open channel: %d", ret); - ret = -1; - goto Cleanup; - } - - /* Channel is opened */ - DPRINT_INFO(NETVSC, "*** NetVSC channel opened successfully! ***"); - - /* Connect with the NetVsp */ - ret = NetVscConnectToVsp(Device); - if (ret != 0) { - DPRINT_ERR(NETVSC, "unable to connect to NetVSP - %d", ret); - ret = -1; - goto Close; - } - - DPRINT_INFO(NETVSC, "*** NetVSC channel handshake result - %d ***", - ret); - - return ret; - -Close: - /* Now, we can close the channel safely */ - Device->Driver->VmbusChannelInterface.Close(Device); - -Cleanup: - - if (netDevice) { - kfree(netDevice->ChannelInitEvent); - - list_for_each_entry_safe(packet, pos, - &netDevice->ReceivePacketList, - ListEntry) { - list_del(&packet->ListEntry); - kfree(packet); - } - - ReleaseOutboundNetDevice(Device); - ReleaseInboundNetDevice(Device); - - FreeNetDevice(netDevice); - } - - return ret; -} - -/* - * NetVscOnDeviceRemove - Callback when the root bus device is removed - */ -static int NetVscOnDeviceRemove(struct hv_device *Device) -{ - struct netvsc_device *netDevice; - struct hv_netvsc_packet *netvscPacket, *pos; - - DPRINT_INFO(NETVSC, "Disabling outbound traffic on net device (%p)...", - Device->Extension); - - /* Stop outbound traffic ie sends and receives completions */ - netDevice = ReleaseOutboundNetDevice(Device); - if (!netDevice) { - DPRINT_ERR(NETVSC, "No net device present!!"); - return -1; - } - - /* Wait for all send completions */ - while (atomic_read(&netDevice->NumOutstandingSends)) { - DPRINT_INFO(NETVSC, "waiting for %d requests to complete...", - atomic_read(&netDevice->NumOutstandingSends)); - udelay(100); - } - - DPRINT_INFO(NETVSC, "Disconnecting from netvsp..."); - - NetVscDisconnectFromVsp(netDevice); - - DPRINT_INFO(NETVSC, "Disabling inbound traffic on net device (%p)...", - Device->Extension); - - /* Stop inbound traffic ie receives and sends completions */ - netDevice = ReleaseInboundNetDevice(Device); - - /* At this point, no one should be accessing netDevice except in here */ - DPRINT_INFO(NETVSC, "net device (%p) safe to remove", netDevice); - - /* Now, we can close the channel safely */ - Device->Driver->VmbusChannelInterface.Close(Device); - - /* Release all resources */ - list_for_each_entry_safe(netvscPacket, pos, - &netDevice->ReceivePacketList, ListEntry) { - list_del(&netvscPacket->ListEntry); - kfree(netvscPacket); - } - - kfree(netDevice->ChannelInitEvent); - FreeNetDevice(netDevice); - return 0; -} - -/* - * NetVscOnCleanup - Perform any cleanup when the driver is removed - */ -static void NetVscOnCleanup(struct hv_driver *drv) -{ -} - -static void NetVscOnSendCompletion(struct hv_device *Device, - struct vmpacket_descriptor *Packet) -{ - struct netvsc_device *netDevice; - struct nvsp_message *nvspPacket; - struct hv_netvsc_packet *nvscPacket; - - netDevice = GetInboundNetDevice(Device); - if (!netDevice) { - DPRINT_ERR(NETVSC, "unable to get net device..." - "device being destroyed?"); - return; - } - - nvspPacket = (struct nvsp_message *)((unsigned long)Packet + (Packet->DataOffset8 << 3)); - - DPRINT_DBG(NETVSC, "send completion packet - type %d", - nvspPacket->Header.MessageType); - - if ((nvspPacket->Header.MessageType == NvspMessageTypeInitComplete) || - (nvspPacket->Header.MessageType == - NvspMessage1TypeSendReceiveBufferComplete) || - (nvspPacket->Header.MessageType == - NvspMessage1TypeSendSendBufferComplete)) { - /* Copy the response back */ - memcpy(&netDevice->ChannelInitPacket, nvspPacket, - sizeof(struct nvsp_message)); - osd_WaitEventSet(netDevice->ChannelInitEvent); - } else if (nvspPacket->Header.MessageType == - NvspMessage1TypeSendRNDISPacketComplete) { - /* Get the send context */ - nvscPacket = (struct hv_netvsc_packet *)(unsigned long)Packet->TransactionId; - /* ASSERT(nvscPacket); */ - - /* Notify the layer above us */ - nvscPacket->Completion.Send.OnSendCompletion(nvscPacket->Completion.Send.SendCompletionContext); - - atomic_dec(&netDevice->NumOutstandingSends); - } else { - DPRINT_ERR(NETVSC, "Unknown send completion packet type - " - "%d received!!", nvspPacket->Header.MessageType); - } - - PutNetDevice(Device); -} - -static int NetVscOnSend(struct hv_device *Device, - struct hv_netvsc_packet *Packet) -{ - struct netvsc_device *netDevice; - int ret = 0; - - struct nvsp_message sendMessage; - - netDevice = GetOutboundNetDevice(Device); - if (!netDevice) { - DPRINT_ERR(NETVSC, "net device (%p) shutting down..." - "ignoring outbound packets", netDevice); - return -2; - } - - sendMessage.Header.MessageType = NvspMessage1TypeSendRNDISPacket; - if (Packet->IsDataPacket) { - /* 0 is RMC_DATA; */ - sendMessage.Messages.Version1Messages.SendRNDISPacket.ChannelType = 0; - } else { - /* 1 is RMC_CONTROL; */ - sendMessage.Messages.Version1Messages.SendRNDISPacket.ChannelType = 1; - } - - /* Not using send buffer section */ - sendMessage.Messages.Version1Messages.SendRNDISPacket.SendBufferSectionIndex = 0xFFFFFFFF; - sendMessage.Messages.Version1Messages.SendRNDISPacket.SendBufferSectionSize = 0; - - if (Packet->PageBufferCount) { - ret = Device->Driver->VmbusChannelInterface.SendPacketPageBuffer( - Device, Packet->PageBuffers, - Packet->PageBufferCount, - &sendMessage, - sizeof(struct nvsp_message), - (unsigned long)Packet); - } else { - ret = Device->Driver->VmbusChannelInterface.SendPacket(Device, - &sendMessage, - sizeof(struct nvsp_message), - (unsigned long)Packet, - VmbusPacketTypeDataInBand, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - - } - - if (ret != 0) - DPRINT_ERR(NETVSC, "Unable to send packet %p ret %d", - Packet, ret); - - atomic_inc(&netDevice->NumOutstandingSends); - PutNetDevice(Device); - return ret; -} - -static void NetVscOnReceive(struct hv_device *Device, - struct vmpacket_descriptor *Packet) -{ - struct netvsc_device *netDevice; - struct vmtransfer_page_packet_header *vmxferpagePacket; - struct nvsp_message *nvspPacket; - struct hv_netvsc_packet *netvscPacket = NULL; - unsigned long start; - unsigned long end, endVirtual; - /* struct netvsc_driver *netvscDriver; */ - struct xferpage_packet *xferpagePacket = NULL; - int i, j; - int count = 0, bytesRemain = 0; - unsigned long flags; - LIST_HEAD(listHead); - - netDevice = GetInboundNetDevice(Device); - if (!netDevice) { - DPRINT_ERR(NETVSC, "unable to get net device..." - "device being destroyed?"); - return; - } - - /* - * All inbound packets other than send completion should be xfer page - * packet - */ - if (Packet->Type != VmbusPacketTypeDataUsingTransferPages) { - DPRINT_ERR(NETVSC, "Unknown packet type received - %d", - Packet->Type); - PutNetDevice(Device); - return; - } - - nvspPacket = (struct nvsp_message *)((unsigned long)Packet + - (Packet->DataOffset8 << 3)); - - /* Make sure this is a valid nvsp packet */ - if (nvspPacket->Header.MessageType != NvspMessage1TypeSendRNDISPacket) { - DPRINT_ERR(NETVSC, "Unknown nvsp packet type received - %d", - nvspPacket->Header.MessageType); - PutNetDevice(Device); - return; - } - - DPRINT_DBG(NETVSC, "NVSP packet received - type %d", - nvspPacket->Header.MessageType); - - vmxferpagePacket = (struct vmtransfer_page_packet_header *)Packet; - - if (vmxferpagePacket->TransferPageSetId != NETVSC_RECEIVE_BUFFER_ID) { - DPRINT_ERR(NETVSC, "Invalid xfer page set id - " - "expecting %x got %x", NETVSC_RECEIVE_BUFFER_ID, - vmxferpagePacket->TransferPageSetId); - PutNetDevice(Device); - return; - } - - DPRINT_DBG(NETVSC, "xfer page - range count %d", - vmxferpagePacket->RangeCount); - - /* - * Grab free packets (range count + 1) to represent this xfer - * page packet. +1 to represent the xfer page packet itself. - * We grab it here so that we know exactly how many we can - * fulfil - */ - spin_lock_irqsave(&netDevice->receive_packet_list_lock, flags); - while (!list_empty(&netDevice->ReceivePacketList)) { - list_move_tail(netDevice->ReceivePacketList.next, &listHead); - if (++count == vmxferpagePacket->RangeCount + 1) - break; - } - spin_unlock_irqrestore(&netDevice->receive_packet_list_lock, flags); - - /* - * We need at least 2 netvsc pkts (1 to represent the xfer - * page and at least 1 for the range) i.e. we can handled - * some of the xfer page packet ranges... - */ - if (count < 2) { - DPRINT_ERR(NETVSC, "Got only %d netvsc pkt...needed %d pkts. " - "Dropping this xfer page packet completely!", - count, vmxferpagePacket->RangeCount + 1); - - /* Return it to the freelist */ - spin_lock_irqsave(&netDevice->receive_packet_list_lock, flags); - for (i = count; i != 0; i--) { - list_move_tail(listHead.next, - &netDevice->ReceivePacketList); - } - spin_unlock_irqrestore(&netDevice->receive_packet_list_lock, - flags); - - NetVscSendReceiveCompletion(Device, - vmxferpagePacket->d.TransactionId); - - PutNetDevice(Device); - return; - } - - /* Remove the 1st packet to represent the xfer page packet itself */ - xferpagePacket = (struct xferpage_packet *)listHead.next; - list_del(&xferpagePacket->ListEntry); - - /* This is how much we can satisfy */ - xferpagePacket->Count = count - 1; - /* ASSERT(xferpagePacket->Count > 0 && xferpagePacket->Count <= */ - /* vmxferpagePacket->RangeCount); */ - - if (xferpagePacket->Count != vmxferpagePacket->RangeCount) { - DPRINT_INFO(NETVSC, "Needed %d netvsc pkts to satisy this xfer " - "page...got %d", vmxferpagePacket->RangeCount, - xferpagePacket->Count); - } - - /* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */ - for (i = 0; i < (count - 1); i++) { - netvscPacket = (struct hv_netvsc_packet *)listHead.next; - list_del(&netvscPacket->ListEntry); - - /* Initialize the netvsc packet */ - netvscPacket->XferPagePacket = xferpagePacket; - netvscPacket->Completion.Recv.OnReceiveCompletion = - NetVscOnReceiveCompletion; - netvscPacket->Completion.Recv.ReceiveCompletionContext = - netvscPacket; - netvscPacket->Device = Device; - /* Save this so that we can send it back */ - netvscPacket->Completion.Recv.ReceiveCompletionTid = - vmxferpagePacket->d.TransactionId; - - netvscPacket->TotalDataBufferLength = - vmxferpagePacket->Ranges[i].ByteCount; - netvscPacket->PageBufferCount = 1; - - /* ASSERT(vmxferpagePacket->Ranges[i].ByteOffset + */ - /* vmxferpagePacket->Ranges[i].ByteCount < */ - /* netDevice->ReceiveBufferSize); */ - - netvscPacket->PageBuffers[0].Length = - vmxferpagePacket->Ranges[i].ByteCount; - - start = virt_to_phys((void *)((unsigned long)netDevice->ReceiveBuffer + vmxferpagePacket->Ranges[i].ByteOffset)); - - netvscPacket->PageBuffers[0].Pfn = start >> PAGE_SHIFT; - endVirtual = (unsigned long)netDevice->ReceiveBuffer - + vmxferpagePacket->Ranges[i].ByteOffset - + vmxferpagePacket->Ranges[i].ByteCount - 1; - end = virt_to_phys((void *)endVirtual); - - /* Calculate the page relative offset */ - netvscPacket->PageBuffers[0].Offset = - vmxferpagePacket->Ranges[i].ByteOffset & (PAGE_SIZE - 1); - if ((end >> PAGE_SHIFT) != (start >> PAGE_SHIFT)) { - /* Handle frame across multiple pages: */ - netvscPacket->PageBuffers[0].Length = - (netvscPacket->PageBuffers[0].Pfn << PAGE_SHIFT) - + PAGE_SIZE - start; - bytesRemain = netvscPacket->TotalDataBufferLength - - netvscPacket->PageBuffers[0].Length; - for (j = 1; j < NETVSC_PACKET_MAXPAGE; j++) { - netvscPacket->PageBuffers[j].Offset = 0; - if (bytesRemain <= PAGE_SIZE) { - netvscPacket->PageBuffers[j].Length = bytesRemain; - bytesRemain = 0; - } else { - netvscPacket->PageBuffers[j].Length = PAGE_SIZE; - bytesRemain -= PAGE_SIZE; - } - netvscPacket->PageBuffers[j].Pfn = - virt_to_phys((void *)(endVirtual - bytesRemain)) >> PAGE_SHIFT; - netvscPacket->PageBufferCount++; - if (bytesRemain == 0) - break; - } - /* ASSERT(bytesRemain == 0); */ - } - DPRINT_DBG(NETVSC, "[%d] - (abs offset %u len %u) => " - "(pfn %llx, offset %u, len %u)", i, - vmxferpagePacket->Ranges[i].ByteOffset, - vmxferpagePacket->Ranges[i].ByteCount, - netvscPacket->PageBuffers[0].Pfn, - netvscPacket->PageBuffers[0].Offset, - netvscPacket->PageBuffers[0].Length); - - /* Pass it to the upper layer */ - ((struct netvsc_driver *)Device->Driver)->OnReceiveCallback(Device, netvscPacket); - - NetVscOnReceiveCompletion(netvscPacket->Completion.Recv.ReceiveCompletionContext); - } - - /* ASSERT(list_empty(&listHead)); */ - - PutNetDevice(Device); -} - -static void NetVscSendReceiveCompletion(struct hv_device *Device, - u64 TransactionId) -{ - struct nvsp_message recvcompMessage; - int retries = 0; - int ret; - - DPRINT_DBG(NETVSC, "Sending receive completion pkt - %llx", - TransactionId); - - recvcompMessage.Header.MessageType = - NvspMessage1TypeSendRNDISPacketComplete; - - /* FIXME: Pass in the status */ - recvcompMessage.Messages.Version1Messages.SendRNDISPacketComplete.Status = NvspStatusSuccess; - -retry_send_cmplt: - /* Send the completion */ - ret = Device->Driver->VmbusChannelInterface.SendPacket(Device, - &recvcompMessage, - sizeof(struct nvsp_message), - TransactionId, - VmbusPacketTypeCompletion, 0); - if (ret == 0) { - /* success */ - /* no-op */ - } else if (ret == -1) { - /* no more room...wait a bit and attempt to retry 3 times */ - retries++; - DPRINT_ERR(NETVSC, "unable to send receive completion pkt " - "(tid %llx)...retrying %d", TransactionId, retries); - - if (retries < 4) { - udelay(100); - goto retry_send_cmplt; - } else { - DPRINT_ERR(NETVSC, "unable to send receive completion " - "pkt (tid %llx)...give up retrying", - TransactionId); - } - } else { - DPRINT_ERR(NETVSC, "unable to send receive completion pkt - " - "%llx", TransactionId); - } -} - -/* Send a receive completion packet to RNDIS device (ie NetVsp) */ -static void NetVscOnReceiveCompletion(void *Context) -{ - struct hv_netvsc_packet *packet = Context; - struct hv_device *device = (struct hv_device *)packet->Device; - struct netvsc_device *netDevice; - u64 transactionId = 0; - bool fSendReceiveComp = false; - unsigned long flags; - - /* ASSERT(packet->XferPagePacket); */ - - /* - * Even though it seems logical to do a GetOutboundNetDevice() here to - * send out receive completion, we are using GetInboundNetDevice() - * since we may have disable outbound traffic already. - */ - netDevice = GetInboundNetDevice(device); - if (!netDevice) { - DPRINT_ERR(NETVSC, "unable to get net device..." - "device being destroyed?"); - return; - } - - /* Overloading use of the lock. */ - spin_lock_irqsave(&netDevice->receive_packet_list_lock, flags); - - /* ASSERT(packet->XferPagePacket->Count > 0); */ - packet->XferPagePacket->Count--; - - /* - * Last one in the line that represent 1 xfer page packet. - * Return the xfer page packet itself to the freelist - */ - if (packet->XferPagePacket->Count == 0) { - fSendReceiveComp = true; - transactionId = packet->Completion.Recv.ReceiveCompletionTid; - list_add_tail(&packet->XferPagePacket->ListEntry, - &netDevice->ReceivePacketList); - - } - - /* Put the packet back */ - list_add_tail(&packet->ListEntry, &netDevice->ReceivePacketList); - spin_unlock_irqrestore(&netDevice->receive_packet_list_lock, flags); - - /* Send a receive completion for the xfer page packet */ - if (fSendReceiveComp) - NetVscSendReceiveCompletion(device, transactionId); - - PutNetDevice(device); -} - -static void NetVscOnChannelCallback(void *Context) -{ - int ret; - struct hv_device *device = Context; - struct netvsc_device *netDevice; - u32 bytesRecvd; - u64 requestId; - unsigned char *packet; - struct vmpacket_descriptor *desc; - unsigned char *buffer; - int bufferlen = NETVSC_PACKET_SIZE; - - /* ASSERT(device); */ - - packet = kzalloc(NETVSC_PACKET_SIZE * sizeof(unsigned char), - GFP_KERNEL); - if (!packet) - return; - buffer = packet; - - netDevice = GetInboundNetDevice(device); - if (!netDevice) { - DPRINT_ERR(NETVSC, "net device (%p) shutting down..." - "ignoring inbound packets", netDevice); - goto out; - } - - do { - ret = device->Driver->VmbusChannelInterface.RecvPacketRaw( - device, buffer, bufferlen, - &bytesRecvd, &requestId); - if (ret == 0) { - if (bytesRecvd > 0) { - DPRINT_DBG(NETVSC, "receive %d bytes, tid %llx", - bytesRecvd, requestId); - - desc = (struct vmpacket_descriptor *)buffer; - switch (desc->Type) { - case VmbusPacketTypeCompletion: - NetVscOnSendCompletion(device, desc); - break; - - case VmbusPacketTypeDataUsingTransferPages: - NetVscOnReceive(device, desc); - break; - - default: - DPRINT_ERR(NETVSC, - "unhandled packet type %d, " - "tid %llx len %d\n", - desc->Type, requestId, - bytesRecvd); - break; - } - - /* reset */ - if (bufferlen > NETVSC_PACKET_SIZE) { - kfree(buffer); - buffer = packet; - bufferlen = NETVSC_PACKET_SIZE; - } - } else { - /* reset */ - if (bufferlen > NETVSC_PACKET_SIZE) { - kfree(buffer); - buffer = packet; - bufferlen = NETVSC_PACKET_SIZE; - } - - break; - } - } else if (ret == -2) { - /* Handle large packet */ - buffer = kmalloc(bytesRecvd, GFP_ATOMIC); - if (buffer == NULL) { - /* Try again next time around */ - DPRINT_ERR(NETVSC, - "unable to allocate buffer of size " - "(%d)!!", bytesRecvd); - break; - } - - bufferlen = bytesRecvd; - } - } while (1); - - PutNetDevice(device); -out: - kfree(buffer); - return; -} diff --git a/drivers/staging/hv/NetVsc.h b/drivers/staging/hv/NetVsc.h deleted file mode 100644 index a6264db..0000000 --- a/drivers/staging/hv/NetVsc.h +++ b/dev/null @@ -1,331 +0,0 @@ -/* - * - * 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> - * - */ - - -#ifndef _NETVSC_H_ -#define _NETVSC_H_ - -#include <linux/list.h> -#include "VmbusPacketFormat.h" -#include "VmbusChannelInterface.h" -#include "NetVscApi.h" - - -#define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF) - -#define NVSP_PROTOCOL_VERSION_1 2 -#define NVSP_MIN_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1 -#define NVSP_MAX_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1 - -enum { - NvspMessageTypeNone = 0, - - /* Init Messages */ - NvspMessageTypeInit = 1, - NvspMessageTypeInitComplete = 2, - - NvspVersionMessageStart = 100, - - /* Version 1 Messages */ - NvspMessage1TypeSendNdisVersion = NvspVersionMessageStart, - - NvspMessage1TypeSendReceiveBuffer, - NvspMessage1TypeSendReceiveBufferComplete, - NvspMessage1TypeRevokeReceiveBuffer, - - NvspMessage1TypeSendSendBuffer, - NvspMessage1TypeSendSendBufferComplete, - NvspMessage1TypeRevokeSendBuffer, - - NvspMessage1TypeSendRNDISPacket, - NvspMessage1TypeSendRNDISPacketComplete, - - /* - * This should be set to the number of messages for the version with - * the maximum number of messages. - */ - NvspNumMessagePerVersion = 9, -}; - -enum { - NvspStatusNone = 0, - NvspStatusSuccess, - NvspStatusFailure, - NvspStatusProtocolVersionRangeTooNew, - NvspStatusProtocolVersionRangeTooOld, - NvspStatusInvalidRndisPacket, - NvspStatusBusy, - NvspStatusMax, -}; - -struct nvsp_message_header { - u32 MessageType; -}; - -/* Init Messages */ - -/* - * This message is used by the VSC to initialize the channel after the channels - * has been opened. This message should never include anything other then - * versioning (i.e. this message will be the same for ever). - */ -struct nvsp_message_init { - u32 MinProtocolVersion; - u32 MaxProtocolVersion; -} __attribute__((packed)); - -/* - * This message is used by the VSP to complete the initialization of the - * channel. This message should never include anything other then versioning - * (i.e. this message will be the same for ever). - */ -struct nvsp_message_init_complete { - u32 NegotiatedProtocolVersion; - u32 MaximumMdlChainLength; - u32 Status; -} __attribute__((packed)); - -union nvsp_message_init_uber { - struct nvsp_message_init Init; - struct nvsp_message_init_complete InitComplete; -} __attribute__((packed)); - -/* Version 1 Messages */ - -/* - * This message is used by the VSC to send the NDIS version to the VSP. The VSP - * can use this information when handling OIDs sent by the VSC. - */ -struct nvsp_1_message_send_ndis_version { - u32 NdisMajorVersion; - u32 NdisMinorVersion; -} __attribute__((packed)); - -/* - * This message is used by the VSC to send a receive buffer to the VSP. The VSP - * can then use the receive buffer to send data to the VSC. - */ -struct nvsp_1_message_send_receive_buffer { - u32 GpadlHandle; - u16 Id; -} __attribute__((packed)); - -struct nvsp_1_receive_buffer_section { - u32 Offset; - u32 SubAllocationSize; - u32 NumSubAllocations; - u32 EndOffset; -} __attribute__((packed)); - -/* - * This message is used by the VSP to acknowledge a receive buffer send by the - * VSC. This message must be sent by the VSP before the VSP uses the receive - * buffer. - */ -struct nvsp_1_message_send_receive_buffer_complete { - u32 Status; - u32 NumSections; - - /* - * The receive buffer is split into two parts, a large suballocation - * section and a small suballocation section. These sections are then - * suballocated by a certain size. - */ - - /* - * For example, the following break up of the receive buffer has 6 - * large suballocations and 10 small suballocations. - */ - - /* - * | Large Section | | Small Section | - * ------------------------------------------------------------ - * | | | | | | | | | | | | | | | | | | - * | | - * LargeOffset SmallOffset - */ - - struct nvsp_1_receive_buffer_section Sections[1]; -} __attribute__((packed)); - -/* - * This message is sent by the VSC to revoke the receive buffer. After the VSP - * completes this transaction, the vsp should never use the receive buffer - * again. - */ -struct nvsp_1_message_revoke_receive_buffer { - u16 Id; -}; - -/* - * This message is used by the VSC to send a send buffer to the VSP. The VSC - * can then use the send buffer to send data to the VSP. - */ -struct nvsp_1_message_send_send_buffer { - u32 GpadlHandle; - u16 Id; -} __attribute__((packed)); - -/* - * This message is used by the VSP to acknowledge a send buffer sent by the - * VSC. This message must be sent by the VSP before the VSP uses the sent - * buffer. - */ -struct nvsp_1_message_send_send_buffer_complete { - u32 Status; - - /* - * The VSC gets to choose the size of the send buffer and the VSP gets - * to choose the sections size of the buffer. This was done to enable - * dynamic reconfigurations when the cost of GPA-direct buffers - * decreases. - */ - u32 SectionSize; -} __attribute__((packed)); - -/* - * This message is sent by the VSC to revoke the send buffer. After the VSP - * completes this transaction, the vsp should never use the send buffer again. - */ -struct nvsp_1_message_revoke_send_buffer { - u16 Id; -}; - -/* - * This message is used by both the VSP and the VSC to send a RNDIS message to - * the opposite channel endpoint. - */ -struct nvsp_1_message_send_rndis_packet { - /* - * This field is specified by RNIDS. They assume there's two different - * channels of communication. However, the Network VSP only has one. - * Therefore, the channel travels with the RNDIS packet. - */ - u32 ChannelType; - - /* - * This field is used to send part or all of the data through a send - * buffer. This values specifies an index into the send buffer. If the - * index is 0xFFFFFFFF, then the send buffer is not being used and all - * of the data was sent through other VMBus mechanisms. - */ - u32 SendBufferSectionIndex; - u32 SendBufferSectionSize; -} __attribute__((packed)); - -/* - * This message is used by both the VSP and the VSC to complete a RNDIS message - * to the opposite channel endpoint. At this point, the initiator of this - * message cannot use any resources associated with the original RNDIS packet. - */ -struct nvsp_1_message_send_rndis_packet_complete { - u32 Status; -}; - -union nvsp_1_message_uber { - struct nvsp_1_message_send_ndis_version SendNdisVersion; - - struct nvsp_1_message_send_receive_buffer SendReceiveBuffer; - struct nvsp_1_message_send_receive_buffer_complete - SendReceiveBufferComplete; - struct nvsp_1_message_revoke_receive_buffer RevokeReceiveBuffer; - - struct nvsp_1_message_send_send_buffer SendSendBuffer; - struct nvsp_1_message_send_send_buffer_complete SendSendBufferComplete; - struct nvsp_1_message_revoke_send_buffer RevokeSendBuffer; - - struct nvsp_1_message_send_rndis_packet SendRNDISPacket; - struct nvsp_1_message_send_rndis_packet_complete - SendRNDISPacketComplete; -} __attribute__((packed)); - -union nvsp_all_messages { - union nvsp_message_init_uber InitMessages; - union nvsp_1_message_uber Version1Messages; -} __attribute__((packed)); - -/* ALL Messages */ -struct nvsp_message { - struct nvsp_message_header Header; - union nvsp_all_messages Messages; -} __attribute__((packed)); - - - - -/* #define NVSC_MIN_PROTOCOL_VERSION 1 */ -/* #define NVSC_MAX_PROTOCOL_VERSION 1 */ - -#define NETVSC_SEND_BUFFER_SIZE (64*1024) /* 64K */ -#define NETVSC_SEND_BUFFER_ID 0xface - - -#define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024) /* 1MB */ - -#define NETVSC_RECEIVE_BUFFER_ID 0xcafe - -#define NETVSC_RECEIVE_SG_COUNT 1 - -/* Preallocated receive packets */ -#define NETVSC_RECEIVE_PACKETLIST_COUNT 256 - -#define NETVSC_PACKET_SIZE 2048 - -/* Per netvsc channel-specific */ -struct netvsc_device { - struct hv_device *Device; - - atomic_t RefCount; - atomic_t NumOutstandingSends; - /* - * List of free preallocated hv_netvsc_packet to represent receive - * packet - */ - struct list_head ReceivePacketList; - spinlock_t receive_packet_list_lock; - - /* Send buffer allocated by us but manages by NetVSP */ - void *SendBuffer; - u32 SendBufferSize; - u32 SendBufferGpadlHandle; - u32 SendSectionSize; - - /* Receive buffer allocated by us but manages by NetVSP */ - void *ReceiveBuffer; - u32 ReceiveBufferSize; - u32 ReceiveBufferGpadlHandle; - u32 ReceiveSectionCount; - struct nvsp_1_receive_buffer_section *ReceiveSections; - - /* Used for NetVSP initialization protocol */ - struct osd_waitevent *ChannelInitEvent; - struct nvsp_message ChannelInitPacket; - - struct nvsp_message RevokePacket; - /* unsigned char HwMacAddr[HW_MACADDR_LEN]; */ - - /* Holds rndis device info */ - void *Extension; -}; - -#endif /* _NETVSC_H_ */ diff --git a/drivers/staging/hv/NetVscApi.h b/drivers/staging/hv/NetVscApi.h deleted file mode 100644 index 91a4cd9..0000000 --- a/drivers/staging/hv/NetVscApi.h +++ b/dev/null @@ -1,116 +0,0 @@ -/* - * - * 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> - * - */ - - -#ifndef _NETVSC_API_H_ -#define _NETVSC_API_H_ - -#include "VmbusApi.h" - -/* Fwd declaration */ -struct hv_netvsc_packet; - -/* Represent the xfer page packet which contains 1 or more netvsc packet */ -struct xferpage_packet { - struct list_head ListEntry; - - /* # of netvsc packets this xfer packet contains */ - u32 Count; -}; - -/* The number of pages which are enough to cover jumbo frame buffer. */ -#define NETVSC_PACKET_MAXPAGE 4 - -/* - * Represent netvsc packet which contains 1 RNDIS and 1 ethernet frame - * within the RNDIS - */ -struct hv_netvsc_packet { - /* Bookkeeping stuff */ - struct list_head ListEntry; - - struct hv_device *Device; - bool IsDataPacket; - - /* - * Valid only for receives when we break a xfer page packet - * into multiple netvsc packets - */ - struct xferpage_packet *XferPagePacket; - - union { - struct{ - u64 ReceiveCompletionTid; - void *ReceiveCompletionContext; - void (*OnReceiveCompletion)(void *context); - } Recv; - struct{ - u64 SendCompletionTid; - void *SendCompletionContext; - void (*OnSendCompletion)(void *context); - } Send; - } Completion; - - /* This points to the memory after PageBuffers */ - void *Extension; - - u32 TotalDataBufferLength; - /* Points to the send/receive buffer where the ethernet frame is */ - u32 PageBufferCount; - struct hv_page_buffer PageBuffers[NETVSC_PACKET_MAXPAGE]; -}; - -/* Represents the net vsc driver */ -struct netvsc_driver { - /* Must be the first field */ - /* Which is a bug FIXME! */ - struct hv_driver Base; - - u32 RingBufferSize; - u32 RequestExtSize; - - /* - * This is set by the caller to allow us to callback when we - * receive a packet from the "wire" - */ - int (*OnReceiveCallback)(struct hv_device *dev, - struct hv_netvsc_packet *packet); - void (*OnLinkStatusChanged)(struct hv_device *dev, u32 Status); - - /* Specific to this driver */ - int (*OnSend)(struct hv_device *dev, struct hv_netvsc_packet *packet); - - void *Context; -}; - -struct netvsc_device_info { - unsigned char MacAddr[6]; - bool LinkState; /* 0 - link up, 1 - link down */ -}; - -/* Interface */ -int NetVscInitialize(struct hv_driver *drv); -int RndisFilterOnOpen(struct hv_device *Device); -int RndisFilterOnClose(struct hv_device *Device); - -#endif /* _NETVSC_API_H_ */ diff --git a/drivers/staging/hv/RingBuffer.c b/drivers/staging/hv/RingBuffer.c deleted file mode 100644 index c4e02a5..0000000 --- a/drivers/staging/hv/RingBuffer.c +++ b/dev/null @@ -1,619 +0,0 @@ -/* - * - * 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 "osd.h" -#include "logging.h" -#include "RingBuffer.h" - - -/* #defines */ - - -/* Amount of space to write to */ -#define BYTES_AVAIL_TO_WRITE(r, w, z) ((w) >= (r)) ? ((z) - ((w) - (r))) : ((r) - (w)) - - -/*++ - -Name: - GetRingBufferAvailBytes() - -Description: - Get number of bytes available to read and to write to - for the specified ring buffer - ---*/ -static inline void -GetRingBufferAvailBytes(struct hv_ring_buffer_info *rbi, u32 *read, u32 *write) -{ - u32 read_loc, write_loc; - - /* Capture the read/write indices before they changed */ - read_loc = rbi->RingBuffer->ReadIndex; - write_loc = rbi->RingBuffer->WriteIndex; - - *write = BYTES_AVAIL_TO_WRITE(read_loc, write_loc, rbi->RingDataSize); - *read = rbi->RingDataSize - *write; -} - -/*++ - -Name: - GetNextWriteLocation() - -Description: - Get the next write location for the specified ring buffer - ---*/ -static inline u32 -GetNextWriteLocation(struct hv_ring_buffer_info *RingInfo) -{ - u32 next = RingInfo->RingBuffer->WriteIndex; - - /* ASSERT(next < RingInfo->RingDataSize); */ - - return next; -} - -/*++ - -Name: - SetNextWriteLocation() - -Description: - Set the next write location for the specified ring buffer - ---*/ -static inline void -SetNextWriteLocation(struct hv_ring_buffer_info *RingInfo, - u32 NextWriteLocation) -{ - RingInfo->RingBuffer->WriteIndex = NextWriteLocation; -} - -/*++ - -Name: - GetNextReadLocation() - -Description: - Get the next read location for the specified ring buffer - ---*/ -static inline u32 -GetNextReadLocation(struct hv_ring_buffer_info *RingInfo) -{ - u32 next = RingInfo->RingBuffer->ReadIndex; - - /* ASSERT(next < RingInfo->RingDataSize); */ - - return next; -} - -/*++ - -Name: - GetNextReadLocationWithOffset() - -Description: - Get the next read location + offset for the specified ring buffer. - This allows the caller to skip - ---*/ -static inline u32 -GetNextReadLocationWithOffset(struct hv_ring_buffer_info *RingInfo, u32 Offset) -{ - u32 next = RingInfo->RingBuffer->ReadIndex; - - /* ASSERT(next < RingInfo->RingDataSize); */ - next += Offset; - next %= RingInfo->RingDataSize; - - return next; -} - -/*++ - -Name: - SetNextReadLocation() - -Description: - Set the next read location for the specified ring buffer - ---*/ -static inline void -SetNextReadLocation(struct hv_ring_buffer_info *RingInfo, u32 NextReadLocation) -{ - RingInfo->RingBuffer->ReadIndex = NextReadLocation; -} - - -/*++ - -Name: - GetRingBuffer() - -Description: - Get the start of the ring buffer - ---*/ -static inline void * -GetRingBuffer(struct hv_ring_buffer_info *RingInfo) -{ - return (void *)RingInfo->RingBuffer->Buffer; -} - - -/*++ - -Name: - GetRingBufferSize() - -Description: - Get the size of the ring buffer - ---*/ -static inline u32 -GetRingBufferSize(struct hv_ring_buffer_info *RingInfo) -{ - return RingInfo->RingDataSize; -} - -/*++ - -Name: - GetRingBufferIndices() - -Description: - Get the read and write indices as u64 of the specified ring buffer - ---*/ -static inline u64 -GetRingBufferIndices(struct hv_ring_buffer_info *RingInfo) -{ - return (u64)RingInfo->RingBuffer->WriteIndex << 32; -} - - -/*++ - -Name: - DumpRingInfo() - -Description: - Dump out to console the ring buffer info - ---*/ -void DumpRingInfo(struct hv_ring_buffer_info *RingInfo, char *Prefix) -{ - u32 bytesAvailToWrite; - u32 bytesAvailToRead; - - GetRingBufferAvailBytes(RingInfo, - &bytesAvailToRead, - &bytesAvailToWrite); - - DPRINT(VMBUS, - DEBUG_RING_LVL, - "%s <<ringinfo %p buffer %p avail write %u " - "avail read %u read idx %u write idx %u>>", - Prefix, - RingInfo, - RingInfo->RingBuffer->Buffer, - bytesAvailToWrite, - bytesAvailToRead, - RingInfo->RingBuffer->ReadIndex, - RingInfo->RingBuffer->WriteIndex); -} - - -/* Internal routines */ - -static u32 -CopyToRingBuffer( - struct hv_ring_buffer_info *RingInfo, - u32 StartWriteOffset, - void *Src, - u32 SrcLen); - -static u32 -CopyFromRingBuffer( - struct hv_ring_buffer_info *RingInfo, - void *Dest, - u32 DestLen, - u32 StartReadOffset); - - - -/*++ - -Name: - RingBufferGetDebugInfo() - -Description: - Get various debug metrics for the specified ring buffer - ---*/ -void RingBufferGetDebugInfo(struct hv_ring_buffer_info *RingInfo, - struct hv_ring_buffer_debug_info *debug_info) -{ - u32 bytesAvailToWrite; - u32 bytesAvailToRead; - - if (RingInfo->RingBuffer) { - GetRingBufferAvailBytes(RingInfo, - &bytesAvailToRead, - &bytesAvailToWrite); - - debug_info->BytesAvailToRead = bytesAvailToRead; - debug_info->BytesAvailToWrite = bytesAvailToWrite; - debug_info->CurrentReadIndex = RingInfo->RingBuffer->ReadIndex; - debug_info->CurrentWriteIndex = RingInfo->RingBuffer->WriteIndex; - debug_info->CurrentInterruptMask = RingInfo->RingBuffer->InterruptMask; - } -} - - -/*++ - -Name: - GetRingBufferInterruptMask() - -Description: - Get the interrupt mask for the specified ring buffer - ---*/ -u32 GetRingBufferInterruptMask(struct hv_ring_buffer_info *rbi) -{ - return rbi->RingBuffer->InterruptMask; -} - -/*++ - -Name: - RingBufferInit() - -Description: - Initialize the ring buffer - ---*/ -int RingBufferInit(struct hv_ring_buffer_info *RingInfo, void *Buffer, u32 BufferLen) -{ - if (sizeof(struct hv_ring_buffer) != PAGE_SIZE) - return -EINVAL; - - memset(RingInfo, 0, sizeof(struct hv_ring_buffer_info)); - - RingInfo->RingBuffer = (struct hv_ring_buffer *)Buffer; - RingInfo->RingBuffer->ReadIndex = RingInfo->RingBuffer->WriteIndex = 0; - - RingInfo->RingSize = BufferLen; - RingInfo->RingDataSize = BufferLen - sizeof(struct hv_ring_buffer); - - spin_lock_init(&RingInfo->ring_lock); - - return 0; -} - -/*++ - -Name: - RingBufferCleanup() - -Description: - Cleanup the ring buffer - ---*/ -void RingBufferCleanup(struct hv_ring_buffer_info *RingInfo) -{ -} - -/*++ - -Name: - RingBufferWrite() - -Description: - Write to the ring buffer - ---*/ -int RingBufferWrite(struct hv_ring_buffer_info *OutRingInfo, - struct scatterlist *sglist, u32 sgcount) -{ - int i = 0; - u32 byteAvailToWrite; - u32 byteAvailToRead; - u32 totalBytesToWrite = 0; - - struct scatterlist *sg; - volatile u32 nextWriteLocation; - u64 prevIndices = 0; - unsigned long flags; - - for_each_sg(sglist, sg, sgcount, i) - { - totalBytesToWrite += sg->length; - } - - totalBytesToWrite += sizeof(u64); - - spin_lock_irqsave(&OutRingInfo->ring_lock, flags); - - GetRingBufferAvailBytes(OutRingInfo, - &byteAvailToRead, - &byteAvailToWrite); - - DPRINT_DBG(VMBUS, "Writing %u bytes...", totalBytesToWrite); - - /* DumpRingInfo(OutRingInfo, "BEFORE "); */ - - /* If there is only room for the packet, assume it is full. */ - /* Otherwise, the next time around, we think the ring buffer */ - /* is empty since the read index == write index */ - if (byteAvailToWrite <= totalBytesToWrite) { - DPRINT_DBG(VMBUS, - "No more space left on outbound ring buffer " - "(needed %u, avail %u)", - totalBytesToWrite, - byteAvailToWrite); - - spin_unlock_irqrestore(&OutRingInfo->ring_lock, flags); - return -1; - } - - /* Write to the ring buffer */ - nextWriteLocation = GetNextWriteLocation(OutRingInfo); - - for_each_sg(sglist, sg, sgcount, i) - { - nextWriteLocation = CopyToRingBuffer(OutRingInfo, - nextWriteLocation, - sg_virt(sg), - sg->length); - } - - /* Set previous packet start */ - prevIndices = GetRingBufferIndices(OutRingInfo); - - nextWriteLocation = CopyToRingBuffer(OutRingInfo, - nextWriteLocation, - &prevIndices, - sizeof(u64)); - - /* Make sure we flush all writes before updating the writeIndex */ - mb(); - - /* Now, update the write location */ - SetNextWriteLocation(OutRingInfo, nextWriteLocation); - - /* DumpRingInfo(OutRingInfo, "AFTER "); */ - - spin_unlock_irqrestore(&OutRingInfo->ring_lock, flags); - return 0; -} - - -/*++ - -Name: - RingBufferPeek() - -Description: - Read without advancing the read index - ---*/ -int RingBufferPeek(struct hv_ring_buffer_info *InRingInfo, void *Buffer, u32 BufferLen) -{ - u32 bytesAvailToWrite; - u32 bytesAvailToRead; - u32 nextReadLocation = 0; - unsigned long flags; - - spin_lock_irqsave(&InRingInfo->ring_lock, flags); - - GetRingBufferAvailBytes(InRingInfo, - &bytesAvailToRead, - &bytesAvailToWrite); - - /* Make sure there is something to read */ - if (bytesAvailToRead < BufferLen) { - /* DPRINT_DBG(VMBUS, - "got callback but not enough to read " - "<avail to read %d read size %d>!!", - bytesAvailToRead, - BufferLen); */ - - spin_unlock_irqrestore(&InRingInfo->ring_lock, flags); - - return -1; - } - - /* Convert to byte offset */ - nextReadLocation = GetNextReadLocation(InRingInfo); - - nextReadLocation = CopyFromRingBuffer(InRingInfo, - Buffer, - BufferLen, - nextReadLocation); - - spin_unlock_irqrestore(&InRingInfo->ring_lock, flags); - - return 0; -} - - -/*++ - -Name: - RingBufferRead() - -Description: - Read and advance the read index - ---*/ -int RingBufferRead(struct hv_ring_buffer_info *InRingInfo, void *Buffer, - u32 BufferLen, u32 Offset) -{ - u32 bytesAvailToWrite; - u32 bytesAvailToRead; - u32 nextReadLocation = 0; - u64 prevIndices = 0; - unsigned long flags; - - if (BufferLen <= 0) - return -EINVAL; - - spin_lock_irqsave(&InRingInfo->ring_lock, flags); - - GetRingBufferAvailBytes(InRingInfo, - &bytesAvailToRead, - &bytesAvailToWrite); - - DPRINT_DBG(VMBUS, "Reading %u bytes...", BufferLen); - - /* DumpRingInfo(InRingInfo, "BEFORE "); */ - - /* Make sure there is something to read */ - if (bytesAvailToRead < BufferLen) { - DPRINT_DBG(VMBUS, - "got callback but not enough to read " - "<avail to read %d read size %d>!!", - bytesAvailToRead, - BufferLen); - - spin_unlock_irqrestore(&InRingInfo->ring_lock, flags); - - return -1; - } - - nextReadLocation = GetNextReadLocationWithOffset(InRingInfo, Offset); - - nextReadLocation = CopyFromRingBuffer(InRingInfo, - Buffer, - BufferLen, - nextReadLocation); - - nextReadLocation = CopyFromRingBuffer(InRingInfo, - &prevIndices, - sizeof(u64), - nextReadLocation); - - /* Make sure all reads are done before we update the read index since */ - /* the writer may start writing to the read area once the read index */ - /*is updated */ - mb(); - - /* Update the read index */ - SetNextReadLocation(InRingInfo, nextReadLocation); - - /* DumpRingInfo(InRingInfo, "AFTER "); */ - - spin_unlock_irqrestore(&InRingInfo->ring_lock, flags); - - return 0; -} - - -/*++ - -Name: - CopyToRingBuffer() - -Description: - Helper routine to copy from source to ring buffer. - Assume there is enough room. Handles wrap-around in dest case only!! - ---*/ -static u32 -CopyToRingBuffer( - struct hv_ring_buffer_info *RingInfo, - u32 StartWriteOffset, - void *Src, - u32 SrcLen) -{ - void *ringBuffer = GetRingBuffer(RingInfo); - u32 ringBufferSize = GetRingBufferSize(RingInfo); - u32 fragLen; - - /* wrap-around detected! */ - if (SrcLen > ringBufferSize - StartWriteOffset) { - DPRINT_DBG(VMBUS, "wrap-around detected!"); - - fragLen = ringBufferSize - StartWriteOffset; - memcpy(ringBuffer + StartWriteOffset, Src, fragLen); - memcpy(ringBuffer, Src + fragLen, SrcLen - fragLen); - } else - memcpy(ringBuffer + StartWriteOffset, Src, SrcLen); - - StartWriteOffset += SrcLen; - StartWriteOffset %= ringBufferSize; - - return StartWriteOffset; -} - - -/*++ - -Name: - CopyFromRingBuffer() - -Description: - Helper routine to copy to source from ring buffer. - Assume there is enough room. Handles wrap-around in src case only!! - ---*/ -static u32 -CopyFromRingBuffer( - struct hv_ring_buffer_info *RingInfo, - void *Dest, - u32 DestLen, - u32 StartReadOffset) -{ - void *ringBuffer = GetRingBuffer(RingInfo); - u32 ringBufferSize = GetRingBufferSize(RingInfo); - - u32 fragLen; - - /* wrap-around detected at the src */ - if (DestLen > ringBufferSize - StartReadOffset) { - DPRINT_DBG(VMBUS, "src wrap-around detected!"); - - fragLen = ringBufferSize - StartReadOffset; - - memcpy(Dest, ringBuffer + StartReadOffset, fragLen); - memcpy(Dest + fragLen, ringBuffer, DestLen - fragLen); - } else - - memcpy(Dest, ringBuffer + StartReadOffset, DestLen); - - - StartReadOffset += DestLen; - StartReadOffset %= ringBufferSize; - - return StartReadOffset; -} - - -/* eof */ diff --git a/drivers/staging/hv/RingBuffer.h b/drivers/staging/hv/RingBuffer.h deleted file mode 100644 index a7f1717..0000000 --- a/drivers/staging/hv/RingBuffer.h +++ b/dev/null @@ -1,102 +0,0 @@ -/* - * - * 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> - * - */ - - -#ifndef _RING_BUFFER_H_ -#define _RING_BUFFER_H_ - -#include <linux/scatterlist.h> - -struct hv_ring_buffer { - /* Offset in bytes from the start of ring data below */ - volatile u32 WriteIndex; - - /* Offset in bytes from the start of ring data below */ - volatile u32 ReadIndex; - - volatile u32 InterruptMask; - - /* Pad it to PAGE_SIZE so that data starts on page boundary */ - u8 Reserved[4084]; - - /* NOTE: - * The InterruptMask field is used only for channels but since our - * vmbus connection also uses this data structure and its data starts - * here, we commented out this field. - */ - /* volatile u32 InterruptMask; */ - - /* - * Ring data starts here + RingDataStartOffset - * !!! DO NOT place any fields below this !!! - */ - u8 Buffer[0]; -} __attribute__((packed)); - -struct hv_ring_buffer_info { - struct hv_ring_buffer *RingBuffer; - u32 RingSize; /* Include the shared header */ - spinlock_t ring_lock; - - u32 RingDataSize; /* < ringSize */ - u32 RingDataStartOffset; -}; - -struct hv_ring_buffer_debug_info { - u32 CurrentInterruptMask; - u32 CurrentReadIndex; - u32 CurrentWriteIndex; - u32 BytesAvailToRead; - u32 BytesAvailToWrite; -}; - - - -/* Interface */ - - -int RingBufferInit(struct hv_ring_buffer_info *RingInfo, void *Buffer, - u32 BufferLen); - -void RingBufferCleanup(struct hv_ring_buffer_info *RingInfo); - -int RingBufferWrite(struct hv_ring_buffer_info *RingInfo, - struct scatterlist *sglist, - u32 sgcount); - -int RingBufferPeek(struct hv_ring_buffer_info *RingInfo, void *Buffer, - u32 BufferLen); - -int RingBufferRead(struct hv_ring_buffer_info *RingInfo, - void *Buffer, - u32 BufferLen, - u32 Offset); - -u32 GetRingBufferInterruptMask(struct hv_ring_buffer_info *RingInfo); - -void DumpRingInfo(struct hv_ring_buffer_info *RingInfo, char *Prefix); - -void RingBufferGetDebugInfo(struct hv_ring_buffer_info *RingInfo, - struct hv_ring_buffer_debug_info *debug_info); - -#endif /* _RING_BUFFER_H_ */ diff --git a/drivers/staging/hv/RndisFilter.c b/drivers/staging/hv/RndisFilter.c deleted file mode 100644 index 159b230..0000000 --- a/drivers/staging/hv/RndisFilter.c +++ b/dev/null @@ -1,918 +0,0 @@ -/* - * 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/highmem.h> -#include <linux/slab.h> -#include <linux/io.h> -#include <linux/if_ether.h> - -#include "osd.h" -#include "logging.h" -#include "NetVscApi.h" -#include "RndisFilter.h" - -/* Data types */ -struct rndis_filter_driver_object { - /* The original driver */ - struct netvsc_driver InnerDriver; -}; - -enum rndis_device_state { - RNDIS_DEV_UNINITIALIZED = 0, - RNDIS_DEV_INITIALIZING, - RNDIS_DEV_INITIALIZED, - RNDIS_DEV_DATAINITIALIZED, -}; - -struct rndis_device { - struct netvsc_device *NetDevice; - - enum rndis_device_state State; - u32 LinkStatus; - atomic_t NewRequestId; - - spinlock_t request_lock; - struct list_head RequestList; - - unsigned char HwMacAddr[ETH_ALEN]; -}; - -struct rndis_request { - struct list_head ListEntry; - struct osd_waitevent *WaitEvent; - - /* - * FIXME: We assumed a fixed size response here. If we do ever need to - * handle a bigger response, we can either define a max response - * message or add a response buffer variable above this field - */ - struct rndis_message ResponseMessage; - - /* Simplify allocation by having a netvsc packet inline */ - struct hv_netvsc_packet Packet; - struct hv_page_buffer Buffer; - /* FIXME: We assumed a fixed size request here. */ - struct rndis_message RequestMessage; -}; - - -struct rndis_filter_packet { - void *CompletionContext; - void (*OnCompletion)(void *context); - struct rndis_message Message; -}; - - -static int RndisFilterOnDeviceAdd(struct hv_device *Device, - void *AdditionalInfo); - -static int RndisFilterOnDeviceRemove(struct hv_device *Device); - -static void RndisFilterOnCleanup(struct hv_driver *Driver); - -static int RndisFilterOnSend(struct hv_device *Device, - struct hv_netvsc_packet *Packet); - -static void RndisFilterOnSendCompletion(void *Context); - -static void RndisFilterOnSendRequestCompletion(void *Context); - - -/* The one and only */ -static struct rndis_filter_driver_object gRndisFilter; - -static struct rndis_device *GetRndisDevice(void) -{ - struct rndis_device *device; - - device = kzalloc(sizeof(struct rndis_device), GFP_KERNEL); - if (!device) - return NULL; - - spin_lock_init(&device->request_lock); - - INIT_LIST_HEAD(&device->RequestList); - - device->State = RNDIS_DEV_UNINITIALIZED; - - return device; -} - -static struct rndis_request *GetRndisRequest(struct rndis_device *Device, - u32 MessageType, - u32 MessageLength) -{ - struct rndis_request *request; - struct rndis_message *rndisMessage; - struct rndis_set_request *set; - unsigned long flags; - - request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL); - if (!request) - return NULL; - - request->WaitEvent = osd_WaitEventCreate(); - if (!request->WaitEvent) { - kfree(request); - return NULL; - } - - rndisMessage = &request->RequestMessage; - rndisMessage->NdisMessageType = MessageType; - rndisMessage->MessageLength = MessageLength; - - /* - * Set the request id. This field is always after the rndis header for - * request/response packet types so we just used the SetRequest as a - * template - */ - set = &rndisMessage->Message.SetRequest; - set->RequestId = atomic_inc_return(&Device->NewRequestId); - - /* Add to the request list */ - spin_lock_irqsave(&Device->request_lock, flags); - list_add_tail(&request->ListEntry, &Device->RequestList); - spin_unlock_irqrestore(&Device->request_lock, flags); - - return request; -} - -static void PutRndisRequest(struct rndis_device *Device, - struct rndis_request *Request) -{ - unsigned long flags; - - spin_lock_irqsave(&Device->request_lock, flags); - list_del(&Request->ListEntry); - spin_unlock_irqrestore(&Device->request_lock, flags); - - kfree(Request->WaitEvent); - kfree(Request); -} - -static void DumpRndisMessage(struct rndis_message *RndisMessage) -{ - switch (RndisMessage->NdisMessageType) { - case REMOTE_NDIS_PACKET_MSG: - DPRINT_DBG(NETVSC, "REMOTE_NDIS_PACKET_MSG (len %u, " - "data offset %u data len %u, # oob %u, " - "oob offset %u, oob len %u, pkt offset %u, " - "pkt len %u", - RndisMessage->MessageLength, - RndisMessage->Message.Packet.DataOffset, - RndisMessage->Message.Packet.DataLength, - RndisMessage->Message.Packet.NumOOBDataElements, - RndisMessage->Message.Packet.OOBDataOffset, - RndisMessage->Message.Packet.OOBDataLength, - RndisMessage->Message.Packet.PerPacketInfoOffset, - RndisMessage->Message.Packet.PerPacketInfoLength); - break; - - case REMOTE_NDIS_INITIALIZE_CMPLT: - DPRINT_DBG(NETVSC, "REMOTE_NDIS_INITIALIZE_CMPLT " - "(len %u, id 0x%x, status 0x%x, major %d, minor %d, " - "device flags %d, max xfer size 0x%x, max pkts %u, " - "pkt aligned %u)", - RndisMessage->MessageLength, - RndisMessage->Message.InitializeComplete.RequestId, - RndisMessage->Message.InitializeComplete.Status, - RndisMessage->Message.InitializeComplete.MajorVersion, - RndisMessage->Message.InitializeComplete.MinorVersion, - RndisMessage->Message.InitializeComplete.DeviceFlags, - RndisMessage->Message.InitializeComplete.MaxTransferSize, - RndisMessage->Message.InitializeComplete.MaxPacketsPerMessage, - RndisMessage->Message.InitializeComplete.PacketAlignmentFactor); - break; - - case REMOTE_NDIS_QUERY_CMPLT: - DPRINT_DBG(NETVSC, "REMOTE_NDIS_QUERY_CMPLT " - "(len %u, id 0x%x, status 0x%x, buf len %u, " - "buf offset %u)", - RndisMessage->MessageLength, - RndisMessage->Message.QueryComplete.RequestId, - RndisMessage->Message.QueryComplete.Status, - RndisMessage->Message.QueryComplete.InformationBufferLength, - RndisMessage->Message.QueryComplete.InformationBufferOffset); - break; - - case REMOTE_NDIS_SET_CMPLT: - DPRINT_DBG(NETVSC, - "REMOTE_NDIS_SET_CMPLT (len %u, id 0x%x, status 0x%x)", - RndisMessage->MessageLength, - RndisMessage->Message.SetComplete.RequestId, - RndisMessage->Message.SetComplete.Status); - break; - - case REMOTE_NDIS_INDICATE_STATUS_MSG: - DPRINT_DBG(NETVSC, "REMOTE_NDIS_INDICATE_STATUS_MSG " - "(len %u, status 0x%x, buf len %u, buf offset %u)", - RndisMessage->MessageLength, - RndisMessage->Message.IndicateStatus.Status, - RndisMessage->Message.IndicateStatus.StatusBufferLength, - RndisMessage->Message.IndicateStatus.StatusBufferOffset); - break; - - default: - DPRINT_DBG(NETVSC, "0x%x (len %u)", - RndisMessage->NdisMessageType, - RndisMessage->MessageLength); - break; - } -} - -static int RndisFilterSendRequest(struct rndis_device *Device, - struct rndis_request *Request) -{ - int ret; - struct hv_netvsc_packet *packet; - - /* Setup the packet to send it */ - packet = &Request->Packet; - - packet->IsDataPacket = false; - packet->TotalDataBufferLength = Request->RequestMessage.MessageLength; - packet->PageBufferCount = 1; - - packet->PageBuffers[0].Pfn = virt_to_phys(&Request->RequestMessage) >> - PAGE_SHIFT; - packet->PageBuffers[0].Length = Request->RequestMessage.MessageLength; - packet->PageBuffers[0].Offset = - (unsigned long)&Request->RequestMessage & (PAGE_SIZE - 1); - - packet->Completion.Send.SendCompletionContext = Request;/* packet; */ - packet->Completion.Send.OnSendCompletion = - RndisFilterOnSendRequestCompletion; - packet->Completion.Send.SendCompletionTid = (unsigned long)Device; - - ret = gRndisFilter.InnerDriver.OnSend(Device->NetDevice->Device, packet); - return ret; -} - -static void RndisFilterReceiveResponse(struct rndis_device *Device, - struct rndis_message *Response) -{ - struct rndis_request *request = NULL; - bool found = false; - unsigned long flags; - - spin_lock_irqsave(&Device->request_lock, flags); - list_for_each_entry(request, &Device->RequestList, ListEntry) { - /* - * All request/response message contains RequestId as the 1st - * field - */ - if (request->RequestMessage.Message.InitializeRequest.RequestId - == Response->Message.InitializeComplete.RequestId) { - DPRINT_DBG(NETVSC, "found rndis request for " - "this response (id 0x%x req type 0x%x res " - "type 0x%x)", - request->RequestMessage.Message.InitializeRequest.RequestId, - request->RequestMessage.NdisMessageType, - Response->NdisMessageType); - - found = true; - break; - } - } - spin_unlock_irqrestore(&Device->request_lock, flags); - - if (found) { - if (Response->MessageLength <= sizeof(struct rndis_message)) { - memcpy(&request->ResponseMessage, Response, - Response->MessageLength); - } else { - DPRINT_ERR(NETVSC, "rndis response buffer overflow " - "detected (size %u max %zu)", - Response->MessageLength, - sizeof(struct rndis_filter_packet)); - - if (Response->NdisMessageType == - REMOTE_NDIS_RESET_CMPLT) { - /* does not have a request id field */ - request->ResponseMessage.Message.ResetComplete.Status = STATUS_BUFFER_OVERFLOW; - } else { - request->ResponseMessage.Message.InitializeComplete.Status = STATUS_BUFFER_OVERFLOW; - } - } - - osd_WaitEventSet(request->WaitEvent); - } else { - DPRINT_ERR(NETVSC, "no rndis request found for this response " - "(id 0x%x res type 0x%x)", - Response->Message.InitializeComplete.RequestId, - Response->NdisMessageType); - } -} - -static void RndisFilterReceiveIndicateStatus(struct rndis_device *Device, - struct rndis_message *Response) -{ - struct rndis_indicate_status *indicate = - &Response->Message.IndicateStatus; - - if (indicate->Status == RNDIS_STATUS_MEDIA_CONNECT) { - gRndisFilter.InnerDriver.OnLinkStatusChanged(Device->NetDevice->Device, 1); - } else if (indicate->Status == RNDIS_STATUS_MEDIA_DISCONNECT) { - gRndisFilter.InnerDriver.OnLinkStatusChanged(Device->NetDevice->Device, 0); - } else { - /* - * TODO: - */ - } -} - -static void RndisFilterReceiveData(struct rndis_device *Device, - struct rndis_message *Message, - struct hv_netvsc_packet *Packet) -{ - struct rndis_packet *rndisPacket; - u32 dataOffset; - - /* empty ethernet frame ?? */ - /* ASSERT(Packet->PageBuffers[0].Length > */ - /* RNDIS_MESSAGE_SIZE(struct rndis_packet)); */ - - rndisPacket = &Message->Message.Packet; - - /* - * FIXME: Handle multiple rndis pkt msgs that maybe enclosed in this - * netvsc packet (ie TotalDataBufferLength != MessageLength) - */ - - /* Remove the rndis header and pass it back up the stack */ - dataOffset = RNDIS_HEADER_SIZE + rndisPacket->DataOffset; - - Packet->TotalDataBufferLength -= dataOffset; - Packet->PageBuffers[0].Offset += dataOffset; - Packet->PageBuffers[0].Length -= dataOffset; - - Packet->IsDataPacket = true; - - gRndisFilter.InnerDriver.OnReceiveCallback(Device->NetDevice->Device, - Packet); -} - -static int RndisFilterOnReceive(struct hv_device *Device, - struct hv_netvsc_packet *Packet) -{ - struct netvsc_device *netDevice = Device->Extension; - struct rndis_device *rndisDevice; - struct rndis_message rndisMessage; - struct rndis_message *rndisHeader; - - if (!netDevice) - return -EINVAL; - - /* Make sure the rndis device state is initialized */ - if (!netDevice->Extension) { - DPRINT_ERR(NETVSC, "got rndis message but no rndis device..." - "dropping this message!"); - return -1; - } - - rndisDevice = (struct rndis_device *)netDevice->Extension; - if (rndisDevice->State == RNDIS_DEV_UNINITIALIZED) { - DPRINT_ERR(NETVSC, "got rndis message but rndis device " - "uninitialized...dropping this message!"); - return -1; - } - - rndisHeader = (struct rndis_message *)kmap_atomic( - pfn_to_page(Packet->PageBuffers[0].Pfn), KM_IRQ0); - - rndisHeader = (void *)((unsigned long)rndisHeader + - Packet->PageBuffers[0].Offset); - - /* Make sure we got a valid rndis message */ - /* - * FIXME: There seems to be a bug in set completion msg where its - * MessageLength is 16 bytes but the ByteCount field in the xfer page - * range shows 52 bytes - * */ -#if 0 - if (Packet->TotalDataBufferLength != rndisHeader->MessageLength) { - kunmap_atomic(rndisHeader - Packet->PageBuffers[0].Offset, - KM_IRQ0); - - DPRINT_ERR(NETVSC, "invalid rndis message? (expected %u " - "bytes got %u)...dropping this message!", - rndisHeader->MessageLength, - Packet->TotalDataBufferLength); - return -1; - } -#endif - - if ((rndisHeader->NdisMessageType != REMOTE_NDIS_PACKET_MSG) && - (rndisHeader->MessageLength > sizeof(struct rndis_message))) { - DPRINT_ERR(NETVSC, "incoming rndis message buffer overflow " - "detected (got %u, max %zu)...marking it an error!", - rndisHeader->MessageLength, - sizeof(struct rndis_message)); - } - - memcpy(&rndisMessage, rndisHeader, - (rndisHeader->MessageLength > sizeof(struct rndis_message)) ? - sizeof(struct rndis_message) : - rndisHeader->MessageLength); - - kunmap_atomic(rndisHeader - Packet->PageBuffers[0].Offset, KM_IRQ0); - - DumpRndisMessage(&rndisMessage); - - switch (rndisMessage.NdisMessageType) { - case REMOTE_NDIS_PACKET_MSG: - /* data msg */ - RndisFilterReceiveData(rndisDevice, &rndisMessage, Packet); - break; - - case REMOTE_NDIS_INITIALIZE_CMPLT: - case REMOTE_NDIS_QUERY_CMPLT: - case REMOTE_NDIS_SET_CMPLT: - /* case REMOTE_NDIS_RESET_CMPLT: */ - /* case REMOTE_NDIS_KEEPALIVE_CMPLT: */ - /* completion msgs */ - RndisFilterReceiveResponse(rndisDevice, &rndisMessage); - break; - - case REMOTE_NDIS_INDICATE_STATUS_MSG: - /* notification msgs */ - RndisFilterReceiveIndicateStatus(rndisDevice, &rndisMessage); - break; - default: - DPRINT_ERR(NETVSC, "unhandled rndis message (type %u len %u)", - rndisMessage.NdisMessageType, - rndisMessage.MessageLength); - break; - } - - return 0; -} - -static int RndisFilterQueryDevice(struct rndis_device *Device, u32 Oid, - void *Result, u32 *ResultSize) -{ - struct rndis_request *request; - u32 inresultSize = *ResultSize; - struct rndis_query_request *query; - struct rndis_query_complete *queryComplete; - int ret = 0; - - if (!Result) - return -EINVAL; - - *ResultSize = 0; - request = GetRndisRequest(Device, REMOTE_NDIS_QUERY_MSG, - RNDIS_MESSAGE_SIZE(struct rndis_query_request)); - if (!request) { - ret = -1; - goto Cleanup; - } - - /* Setup the rndis query */ - query = &request->RequestMessage.Message.QueryRequest; - query->Oid = Oid; - query->InformationBufferOffset = sizeof(struct rndis_query_request); - query->InformationBufferLength = 0; - query->DeviceVcHandle = 0; - - ret = RndisFilterSendRequest(Device, request); - if (ret != 0) - goto Cleanup; - - osd_WaitEventWait(request->WaitEvent); - - /* Copy the response back */ - queryComplete = &request->ResponseMessage.Message.QueryComplete; - - if (queryComplete->InformationBufferLength > inresultSize) { - ret = -1; - goto Cleanup; - } - - memcpy(Result, - (void *)((unsigned long)queryComplete + - queryComplete->InformationBufferOffset), - queryComplete->InformationBufferLength); - - *ResultSize = queryComplete->InformationBufferLength; - -Cleanup: - if (request) - PutRndisRequest(Device, request); - - return ret; -} - -static int RndisFilterQueryDeviceMac(struct rndis_device *Device) -{ - u32 size = ETH_ALEN; - - return RndisFilterQueryDevice(Device, - RNDIS_OID_802_3_PERMANENT_ADDRESS, - Device->HwMacAddr, &size); -} - -static int RndisFilterQueryDeviceLinkStatus(struct rndis_device *Device) -{ - u32 size = sizeof(u32); - - return RndisFilterQueryDevice(Device, - RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, - &Device->LinkStatus, &size); -} - -static int RndisFilterSetPacketFilter(struct rndis_device *Device, - u32 NewFilter) -{ - struct rndis_request *request; - struct rndis_set_request *set; - struct rndis_set_complete *setComplete; - u32 status; - int ret; - - /* ASSERT(RNDIS_MESSAGE_SIZE(struct rndis_set_request) + sizeof(u32) <= */ - /* sizeof(struct rndis_message)); */ - - request = GetRndisRequest(Device, REMOTE_NDIS_SET_MSG, - RNDIS_MESSAGE_SIZE(struct rndis_set_request) + - sizeof(u32)); - if (!request) { - ret = -1; - goto Cleanup; - } - - /* Setup the rndis set */ - set = &request->RequestMessage.Message.SetRequest; - set->Oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER; - set->InformationBufferLength = sizeof(u32); - set->InformationBufferOffset = sizeof(struct rndis_set_request); - - memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request), - &NewFilter, sizeof(u32)); - - ret = RndisFilterSendRequest(Device, request); - if (ret != 0) - goto Cleanup; - - ret = osd_WaitEventWaitEx(request->WaitEvent, 2000/*2sec*/); - if (!ret) { - ret = -1; - DPRINT_ERR(NETVSC, "timeout before we got a set response..."); - /* - * We cant deallocate the request since we may still receive a - * send completion for it. - */ - goto Exit; - } else { - if (ret > 0) - ret = 0; - setComplete = &request->ResponseMessage.Message.SetComplete; - status = setComplete->Status; - } - -Cleanup: - if (request) - PutRndisRequest(Device, request); -Exit: - return ret; -} - -int RndisFilterInit(struct netvsc_driver *Driver) -{ - DPRINT_DBG(NETVSC, "sizeof(struct rndis_filter_packet) == %zd", - sizeof(struct rndis_filter_packet)); - - Driver->RequestExtSize = sizeof(struct rndis_filter_packet); - - /* Driver->Context = rndisDriver; */ - - memset(&gRndisFilter, 0, sizeof(struct rndis_filter_driver_object)); - - /*rndisDriver->Driver = Driver; - - ASSERT(Driver->OnLinkStatusChanged); - rndisDriver->OnLinkStatusChanged = Driver->OnLinkStatusChanged;*/ - - /* Save the original dispatch handlers before we override it */ - gRndisFilter.InnerDriver.Base.OnDeviceAdd = Driver->Base.OnDeviceAdd; - gRndisFilter.InnerDriver.Base.OnDeviceRemove = - Driver->Base.OnDeviceRemove; - gRndisFilter.InnerDriver.Base.OnCleanup = Driver->Base.OnCleanup; - - /* ASSERT(Driver->OnSend); */ - /* ASSERT(Driver->OnReceiveCallback); */ - gRndisFilter.InnerDriver.OnSend = Driver->OnSend; - gRndisFilter.InnerDriver.OnReceiveCallback = Driver->OnReceiveCallback; - gRndisFilter.InnerDriver.OnLinkStatusChanged = - Driver->OnLinkStatusChanged; - - /* Override */ - Driver->Base.OnDeviceAdd = RndisFilterOnDeviceAdd; - Driver->Base.OnDeviceRemove = RndisFilterOnDeviceRemove; - Driver->Base.OnCleanup = RndisFilterOnCleanup; - Driver->OnSend = RndisFilterOnSend; - /* Driver->QueryLinkStatus = RndisFilterQueryDeviceLinkStatus; */ - Driver->OnReceiveCallback = RndisFilterOnReceive; - - return 0; -} - -static int RndisFilterInitDevice(struct rndis_device *Device) -{ - struct rndis_request *request; - struct rndis_initialize_request *init; - struct rndis_initialize_complete *initComplete; - u32 status; - int ret; - - request = GetRndisRequest(Device, REMOTE_NDIS_INITIALIZE_MSG, - RNDIS_MESSAGE_SIZE(struct rndis_initialize_request)); - if (!request) { - ret = -1; - goto Cleanup; - } - - /* Setup the rndis set */ - init = &request->RequestMessage.Message.InitializeRequest; - init->MajorVersion = RNDIS_MAJOR_VERSION; - init->MinorVersion = RNDIS_MINOR_VERSION; - /* FIXME: Use 1536 - rounded ethernet frame size */ - init->MaxTransferSize = 2048; - - Device->State = RNDIS_DEV_INITIALIZING; - - ret = RndisFilterSendRequest(Device, request); - if (ret != 0) { - Device->State = RNDIS_DEV_UNINITIALIZED; - goto Cleanup; - } - - osd_WaitEventWait(request->WaitEvent); - - initComplete = &request->ResponseMessage.Message.InitializeComplete; - status = initComplete->Status; - if (status == RNDIS_STATUS_SUCCESS) { - Device->State = RNDIS_DEV_INITIALIZED; - ret = 0; - } else { - Device->State = RNDIS_DEV_UNINITIALIZED; - ret = -1; - } - -Cleanup: - if (request) - PutRndisRequest(Device, request); - - return ret; -} - -static void RndisFilterHaltDevice(struct rndis_device *Device) -{ - struct rndis_request *request; - struct rndis_halt_request *halt; - - /* Attempt to do a rndis device halt */ - request = GetRndisRequest(Device, REMOTE_NDIS_HALT_MSG, - RNDIS_MESSAGE_SIZE(struct rndis_halt_request)); - if (!request) - goto Cleanup; - - /* Setup the rndis set */ - halt = &request->RequestMessage.Message.HaltRequest; - halt->RequestId = atomic_inc_return(&Device->NewRequestId); - - /* Ignore return since this msg is optional. */ - RndisFilterSendRequest(Device, request); - - Device->State = RNDIS_DEV_UNINITIALIZED; - -Cleanup: - if (request) - PutRndisRequest(Device, request); - return; -} - -static int RndisFilterOpenDevice(struct rndis_device *Device) -{ - int ret; - - if (Device->State != RNDIS_DEV_INITIALIZED) - return 0; - - ret = RndisFilterSetPacketFilter(Device, - NDIS_PACKET_TYPE_BROADCAST | - NDIS_PACKET_TYPE_ALL_MULTICAST | - NDIS_PACKET_TYPE_DIRECTED); - if (ret == 0) - Device->State = RNDIS_DEV_DATAINITIALIZED; - - return ret; -} - -static int RndisFilterCloseDevice(struct rndis_device *Device) -{ - int ret; - - if (Device->State != RNDIS_DEV_DATAINITIALIZED) - return 0; - - ret = RndisFilterSetPacketFilter(Device, 0); - if (ret == 0) - Device->State = RNDIS_DEV_INITIALIZED; - - return ret; -} - -static int RndisFilterOnDeviceAdd(struct hv_device *Device, - void *AdditionalInfo) -{ - int ret; - struct netvsc_device *netDevice; - struct rndis_device *rndisDevice; - struct netvsc_device_info *deviceInfo = AdditionalInfo; - - rndisDevice = GetRndisDevice(); - if (!rndisDevice) - return -1; - - DPRINT_DBG(NETVSC, "rndis device object allocated - %p", rndisDevice); - - /* - * Let the inner driver handle this first to create the netvsc channel - * NOTE! Once the channel is created, we may get a receive callback - * (RndisFilterOnReceive()) before this call is completed - */ - ret = gRndisFilter.InnerDriver.Base.OnDeviceAdd(Device, AdditionalInfo); - if (ret != 0) { - kfree(rndisDevice); - return ret; - } - - - /* Initialize the rndis device */ - netDevice = Device->Extension; - /* ASSERT(netDevice); */ - /* ASSERT(netDevice->Device); */ - - netDevice->Extension = rndisDevice; - rndisDevice->NetDevice = netDevice; - - /* Send the rndis initialization message */ - ret = RndisFilterInitDevice(rndisDevice); - if (ret != 0) { - /* - * TODO: If rndis init failed, we will need to shut down the - * channel - */ - } - - /* Get the mac address */ - ret = RndisFilterQueryDeviceMac(rndisDevice); - if (ret != 0) { - /* - * TODO: shutdown rndis device and the channel - */ - } - - DPRINT_INFO(NETVSC, "Device 0x%p mac addr %pM", - rndisDevice, rndisDevice->HwMacAddr); - - memcpy(deviceInfo->MacAddr, rndisDevice->HwMacAddr, ETH_ALEN); - - RndisFilterQueryDeviceLinkStatus(rndisDevice); - - deviceInfo->LinkState = rndisDevice->LinkStatus; - DPRINT_INFO(NETVSC, "Device 0x%p link state %s", rndisDevice, - ((deviceInfo->LinkState) ? ("down") : ("up"))); - - return ret; -} - -static int RndisFilterOnDeviceRemove(struct hv_device *Device) -{ - struct netvsc_device *netDevice = Device->Extension; - struct rndis_device *rndisDevice = netDevice->Extension; - - /* Halt and release the rndis device */ - RndisFilterHaltDevice(rndisDevice); - - kfree(rndisDevice); - netDevice->Extension = NULL; - - /* Pass control to inner driver to remove the device */ - gRndisFilter.InnerDriver.Base.OnDeviceRemove(Device); - - return 0; -} - -static void RndisFilterOnCleanup(struct hv_driver *Driver) -{ -} - -int RndisFilterOnOpen(struct hv_device *Device) -{ - struct netvsc_device *netDevice = Device->Extension; - - if (!netDevice) - return -EINVAL; - - return RndisFilterOpenDevice(netDevice->Extension); -} - -int RndisFilterOnClose(struct hv_device *Device) -{ - struct netvsc_device *netDevice = Device->Extension; - - if (!netDevice) - return -EINVAL; - - return RndisFilterCloseDevice(netDevice->Extension); -} - -static int RndisFilterOnSend(struct hv_device *Device, - struct hv_netvsc_packet *Packet) -{ - int ret; - struct rndis_filter_packet *filterPacket; - struct rndis_message *rndisMessage; - struct rndis_packet *rndisPacket; - u32 rndisMessageSize; - - /* Add the rndis header */ - filterPacket = (struct rndis_filter_packet *)Packet->Extension; - /* ASSERT(filterPacket); */ - - memset(filterPacket, 0, sizeof(struct rndis_filter_packet)); - - rndisMessage = &filterPacket->Message; - rndisMessageSize = RNDIS_MESSAGE_SIZE(struct rndis_packet); - - rndisMessage->NdisMessageType = REMOTE_NDIS_PACKET_MSG; - rndisMessage->MessageLength = Packet->TotalDataBufferLength + - rndisMessageSize; - - rndisPacket = &rndisMessage->Message.Packet; - rndisPacket->DataOffset = sizeof(struct rndis_packet); - rndisPacket->DataLength = Packet->TotalDataBufferLength; - - Packet->IsDataPacket = true; - Packet->PageBuffers[0].Pfn = virt_to_phys(rndisMessage) >> PAGE_SHIFT; - Packet->PageBuffers[0].Offset = - (unsigned long)rndisMessage & (PAGE_SIZE-1); - Packet->PageBuffers[0].Length = rndisMessageSize; - - /* Save the packet send completion and context */ - filterPacket->OnCompletion = Packet->Completion.Send.OnSendCompletion; - filterPacket->CompletionContext = - Packet->Completion.Send.SendCompletionContext; - - /* Use ours */ - Packet->Completion.Send.OnSendCompletion = RndisFilterOnSendCompletion; - Packet->Completion.Send.SendCompletionContext = filterPacket; - - ret = gRndisFilter.InnerDriver.OnSend(Device, Packet); - if (ret != 0) { - /* - * Reset the completion to originals to allow retries from - * above - */ - Packet->Completion.Send.OnSendCompletion = - filterPacket->OnCompletion; - Packet->Completion.Send.SendCompletionContext = - filterPacket->CompletionContext; - } - - return ret; -} - -static void RndisFilterOnSendCompletion(void *Context) -{ - struct rndis_filter_packet *filterPacket = Context; - - /* Pass it back to the original handler */ - filterPacket->OnCompletion(filterPacket->CompletionContext); -} - - -static void RndisFilterOnSendRequestCompletion(void *Context) -{ - /* Noop */ -} diff --git a/drivers/staging/hv/RndisFilter.h b/drivers/staging/hv/RndisFilter.h deleted file mode 100644 index fa7dd79..0000000 --- a/drivers/staging/hv/RndisFilter.h +++ b/dev/null @@ -1,55 +0,0 @@ -/* - * - * 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> - * - */ - - -#ifndef _RNDISFILTER_H_ -#define _RNDISFILTER_H_ - -#define __struct_bcount(x) - -#include "NetVsc.h" - -#include "rndis.h" - -#define RNDIS_HEADER_SIZE (sizeof(struct rndis_message) - \ - sizeof(union rndis_message_container)) - -#define NDIS_PACKET_TYPE_DIRECTED 0x00000001 -#define NDIS_PACKET_TYPE_MULTICAST 0x00000002 -#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 -#define NDIS_PACKET_TYPE_BROADCAST 0x00000008 -#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 -#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 -#define NDIS_PACKET_TYPE_SMT 0x00000040 -#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 -#define NDIS_PACKET_TYPE_GROUP 0x00000100 -#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200 -#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 -#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 - - -/* Interface */ - -extern int RndisFilterInit(struct netvsc_driver *driver); - -#endif /* _RNDISFILTER_H_ */ diff --git a/drivers/staging/hv/StorVsc.c b/drivers/staging/hv/StorVsc.c deleted file mode 100644 index 57bd315..0000000 --- a/drivers/staging/hv/StorVsc.c +++ b/dev/null @@ -1,856 +0,0 @@ -/* - * 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/string.h> -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/delay.h> -#include "osd.h" -#include "logging.h" -#include "StorVscApi.h" -#include "VmbusPacketFormat.h" -#include "vstorage.h" - - -struct storvsc_request_extension { - /* LIST_ENTRY ListEntry; */ - - struct hv_storvsc_request *Request; - struct hv_device *Device; - - /* Synchronize the request/response if needed */ - struct osd_waitevent *WaitEvent; - - struct vstor_packet VStorPacket; -}; - -/* A storvsc device is a device object that contains a vmbus channel */ -struct storvsc_device { - struct hv_device *Device; - - /* 0 indicates the device is being destroyed */ - atomic_t RefCount; - - int reset; - spinlock_t lock; - atomic_t NumOutstandingRequests; - - /* - * Each unique Port/Path/Target represents 1 channel ie scsi - * controller. In reality, the pathid, targetid is always 0 - * and the port is set by us - */ - unsigned int PortNumber; - unsigned char PathId; - unsigned char TargetId; - - /* LIST_ENTRY OutstandingRequestList; */ - /* HANDLE OutstandingRequestLock; */ - - /* Used for vsc/vsp channel reset process */ - struct storvsc_request_extension InitRequest; - struct storvsc_request_extension ResetRequest; -}; - - -static const char *gDriverName = "storvsc"; - -/* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */ -static const struct hv_guid gStorVscDeviceType = { - .data = { - 0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, - 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f - } -}; - - -static inline struct storvsc_device *AllocStorDevice(struct hv_device *Device) -{ - struct storvsc_device *storDevice; - - storDevice = kzalloc(sizeof(struct storvsc_device), GFP_KERNEL); - if (!storDevice) - return NULL; - - /* Set to 2 to allow both inbound and outbound traffics */ - /* (ie GetStorDevice() and MustGetStorDevice()) to proceed. */ - atomic_cmpxchg(&storDevice->RefCount, 0, 2); - - storDevice->Device = Device; - storDevice->reset = 0; - spin_lock_init(&storDevice->lock); - - Device->Extension = storDevice; - - return storDevice; -} - -static inline void FreeStorDevice(struct storvsc_device *Device) -{ - /* ASSERT(atomic_read(&Device->RefCount) == 0); */ - /*kfree(Device->lock);*/ - kfree(Device); -} - -/* Get the stordevice object iff exists and its refcount > 1 */ -static inline struct storvsc_device *GetStorDevice(struct hv_device *Device) -{ - struct storvsc_device *storDevice; - unsigned long flags; - - storDevice = (struct storvsc_device *)Device->Extension; - - spin_lock_irqsave(&storDevice->lock, flags); - - if (storDevice->reset == 1) { - spin_unlock_irqrestore(&storDevice->lock, flags); - return NULL; - } - - if (storDevice && atomic_read(&storDevice->RefCount) > 1) - atomic_inc(&storDevice->RefCount); - else - storDevice = NULL; - - spin_unlock_irqrestore(&storDevice->lock, flags); - - return storDevice; -} - -/* Get the stordevice object iff exists and its refcount > 0 */ -static inline struct storvsc_device *MustGetStorDevice(struct hv_device *Device) -{ - struct storvsc_device *storDevice; - unsigned long flags; - - storDevice = (struct storvsc_device *)Device->Extension; - - spin_lock_irqsave(&storDevice->lock, flags); - - if (storDevice && atomic_read(&storDevice->RefCount)) - atomic_inc(&storDevice->RefCount); - else - storDevice = NULL; - - spin_unlock_irqrestore(&storDevice->lock, flags); - - return storDevice; -} - -static inline void PutStorDevice(struct hv_device *Device) -{ - struct storvsc_device *storDevice; - - storDevice = (struct storvsc_device *)Device->Extension; - /* ASSERT(storDevice); */ - - atomic_dec(&storDevice->RefCount); - /* ASSERT(atomic_read(&storDevice->RefCount)); */ -} - -/* Drop ref count to 1 to effectively disable GetStorDevice() */ -static inline struct storvsc_device *ReleaseStorDevice(struct hv_device *Device) -{ - struct storvsc_device *storDevice; - - storDevice = (struct storvsc_device *)Device->Extension; - /* ASSERT(storDevice); */ - - /* Busy wait until the ref drop to 2, then set it to 1 */ - while (atomic_cmpxchg(&storDevice->RefCount, 2, 1) != 2) - udelay(100); - - return storDevice; -} - -/* Drop ref count to 0. No one can use StorDevice object. */ -static inline struct storvsc_device *FinalReleaseStorDevice( - struct hv_device *Device) -{ - struct storvsc_device *storDevice; - - storDevice = (struct storvsc_device *)Device->Extension; - /* ASSERT(storDevice); */ - - /* Busy wait until the ref drop to 1, then set it to 0 */ - while (atomic_cmpxchg(&storDevice->RefCount, 1, 0) != 1) - udelay(100); - - Device->Extension = NULL; - return storDevice; -} - -static int StorVscChannelInit(struct hv_device *Device) -{ - struct storvsc_device *storDevice; - struct storvsc_request_extension *request; - struct vstor_packet *vstorPacket; - int ret; - - storDevice = GetStorDevice(Device); - if (!storDevice) { - DPRINT_ERR(STORVSC, "unable to get stor device..." - "device being destroyed?"); - return -1; - } - - request = &storDevice->InitRequest; - vstorPacket = &request->VStorPacket; - - /* - * Now, initiate the vsc/vsp initialization protocol on the open - * channel - */ - memset(request, 0, sizeof(struct storvsc_request_extension)); - request->WaitEvent = osd_WaitEventCreate(); - if (!request->WaitEvent) { - ret = -ENOMEM; - goto nomem; - } - - vstorPacket->Operation = VStorOperationBeginInitialization; - vstorPacket->Flags = REQUEST_COMPLETION_FLAG; - - /*SpinlockAcquire(gDriverExt.packetListLock); - INSERT_TAIL_LIST(&gDriverExt.packetList, &packet->listEntry.entry); - SpinlockRelease(gDriverExt.packetListLock);*/ - - DPRINT_INFO(STORVSC, "BEGIN_INITIALIZATION_OPERATION..."); - - ret = Device->Driver->VmbusChannelInterface.SendPacket(Device, - vstorPacket, - sizeof(struct vstor_packet), - (unsigned long)request, - VmbusPacketTypeDataInBand, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret != 0) { - DPRINT_ERR(STORVSC, - "unable to send BEGIN_INITIALIZATION_OPERATION"); - goto Cleanup; - } - - osd_WaitEventWait(request->WaitEvent); - - if (vstorPacket->Operation != VStorOperationCompleteIo || - vstorPacket->Status != 0) { - DPRINT_ERR(STORVSC, "BEGIN_INITIALIZATION_OPERATION failed " - "(op %d status 0x%x)", - vstorPacket->Operation, vstorPacket->Status); - goto Cleanup; - } - - DPRINT_INFO(STORVSC, "QUERY_PROTOCOL_VERSION_OPERATION..."); - - /* reuse the packet for version range supported */ - memset(vstorPacket, 0, sizeof(struct vstor_packet)); - vstorPacket->Operation = VStorOperationQueryProtocolVersion; - vstorPacket->Flags = REQUEST_COMPLETION_FLAG; - - vstorPacket->Version.MajorMinor = VMSTOR_PROTOCOL_VERSION_CURRENT; - FILL_VMSTOR_REVISION(vstorPacket->Version.Revision); - - ret = Device->Driver->VmbusChannelInterface.SendPacket(Device, - vstorPacket, - sizeof(struct vstor_packet), - (unsigned long)request, - VmbusPacketTypeDataInBand, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret != 0) { - DPRINT_ERR(STORVSC, - "unable to send BEGIN_INITIALIZATION_OPERATION"); - goto Cleanup; - } - - osd_WaitEventWait(request->WaitEvent); - - /* TODO: Check returned version */ - if (vstorPacket->Operation != VStorOperationCompleteIo || - vstorPacket->Status != 0) { - DPRINT_ERR(STORVSC, "QUERY_PROTOCOL_VERSION_OPERATION failed " - "(op %d status 0x%x)", - vstorPacket->Operation, vstorPacket->Status); - goto Cleanup; - } - - /* Query channel properties */ - DPRINT_INFO(STORVSC, "QUERY_PROPERTIES_OPERATION..."); - - memset(vstorPacket, 0, sizeof(struct vstor_packet)); - vstorPacket->Operation = VStorOperationQueryProperties; - vstorPacket->Flags = REQUEST_COMPLETION_FLAG; - vstorPacket->StorageChannelProperties.PortNumber = - storDevice->PortNumber; - - ret = Device->Driver->VmbusChannelInterface.SendPacket(Device, - vstorPacket, - sizeof(struct vstor_packet), - (unsigned long)request, - VmbusPacketTypeDataInBand, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - - if (ret != 0) { - DPRINT_ERR(STORVSC, - "unable to send QUERY_PROPERTIES_OPERATION"); - goto Cleanup; - } - - osd_WaitEventWait(request->WaitEvent); - - /* TODO: Check returned version */ - if (vstorPacket->Operation != VStorOperationCompleteIo || - vstorPacket->Status != 0) { - DPRINT_ERR(STORVSC, "QUERY_PROPERTIES_OPERATION failed " - "(op %d status 0x%x)", - vstorPacket->Operation, vstorPacket->Status); - goto Cleanup; - } - - storDevice->PathId = vstorPacket->StorageChannelProperties.PathId; - storDevice->TargetId = vstorPacket->StorageChannelProperties.TargetId; - - DPRINT_DBG(STORVSC, "channel flag 0x%x, max xfer len 0x%x", - vstorPacket->StorageChannelProperties.Flags, - vstorPacket->StorageChannelProperties.MaxTransferBytes); - - DPRINT_INFO(STORVSC, "END_INITIALIZATION_OPERATION..."); - - memset(vstorPacket, 0, sizeof(struct vstor_packet)); - vstorPacket->Operation = VStorOperationEndInitialization; - vstorPacket->Flags = REQUEST_COMPLETION_FLAG; - - ret = Device->Driver->VmbusChannelInterface.SendPacket(Device, - vstorPacket, - sizeof(struct vstor_packet), - (unsigned long)request, - VmbusPacketTypeDataInBand, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - - if (ret != 0) { - DPRINT_ERR(STORVSC, - "unable to send END_INITIALIZATION_OPERATION"); - goto Cleanup; - } - - osd_WaitEventWait(request->WaitEvent); - - if (vstorPacket->Operation != VStorOperationCompleteIo || - vstorPacket->Status != 0) { - DPRINT_ERR(STORVSC, "END_INITIALIZATION_OPERATION failed " - "(op %d status 0x%x)", - vstorPacket->Operation, vstorPacket->Status); - goto Cleanup; - } - - DPRINT_INFO(STORVSC, "**** storage channel up and running!! ****"); - -Cleanup: - kfree(request->WaitEvent); - request->WaitEvent = NULL; -nomem: - PutStorDevice(Device); - return ret; -} - -static void StorVscOnIOCompletion(struct hv_device *Device, - struct vstor_packet *VStorPacket, - struct storvsc_request_extension *RequestExt) -{ - struct hv_storvsc_request *request; - struct storvsc_device *storDevice; - - storDevice = MustGetStorDevice(Device); - if (!storDevice) { - DPRINT_ERR(STORVSC, "unable to get stor device..." - "device being destroyed?"); - return; - } - - DPRINT_DBG(STORVSC, "IO_COMPLETE_OPERATION - request extension %p " - "completed bytes xfer %u", RequestExt, - VStorPacket->VmSrb.DataTransferLength); - - /* ASSERT(RequestExt != NULL); */ - /* ASSERT(RequestExt->Request != NULL); */ - - request = RequestExt->Request; - - /* ASSERT(request->OnIOCompletion != NULL); */ - - /* Copy over the status...etc */ - request->Status = VStorPacket->VmSrb.ScsiStatus; - - if (request->Status != 0 || VStorPacket->VmSrb.SrbStatus != 1) { - DPRINT_WARN(STORVSC, - "cmd 0x%x scsi status 0x%x srb status 0x%x\n", - request->Cdb[0], VStorPacket->VmSrb.ScsiStatus, - VStorPacket->VmSrb.SrbStatus); - } - - if ((request->Status & 0xFF) == 0x02) { - /* CHECK_CONDITION */ - if (VStorPacket->VmSrb.SrbStatus & 0x80) { - /* autosense data available */ - DPRINT_WARN(STORVSC, "storvsc pkt %p autosense data " - "valid - len %d\n", RequestExt, - VStorPacket->VmSrb.SenseInfoLength); - - /* ASSERT(VStorPacket->VmSrb.SenseInfoLength <= */ - /* request->SenseBufferSize); */ - memcpy(request->SenseBuffer, - VStorPacket->VmSrb.SenseData, - VStorPacket->VmSrb.SenseInfoLength); - - request->SenseBufferSize = - VStorPacket->VmSrb.SenseInfoLength; - } - } - - /* TODO: */ - request->BytesXfer = VStorPacket->VmSrb.DataTransferLength; - - request->OnIOCompletion(request); - - atomic_dec(&storDevice->NumOutstandingRequests); - - PutStorDevice(Device); -} - -static void StorVscOnReceive(struct hv_device *Device, - struct vstor_packet *VStorPacket, - struct storvsc_request_extension *RequestExt) -{ - switch (VStorPacket->Operation) { - case VStorOperationCompleteIo: - DPRINT_DBG(STORVSC, "IO_COMPLETE_OPERATION"); - StorVscOnIOCompletion(Device, VStorPacket, RequestExt); - break; - case VStorOperationRemoveDevice: - DPRINT_INFO(STORVSC, "REMOVE_DEVICE_OPERATION"); - /* TODO: */ - break; - - default: - DPRINT_INFO(STORVSC, "Unknown operation received - %d", - VStorPacket->Operation); - break; - } -} - -static void StorVscOnChannelCallback(void *context) -{ - struct hv_device *device = (struct hv_device *)context; - struct storvsc_device *storDevice; - u32 bytesRecvd; - u64 requestId; - unsigned char packet[ALIGN_UP(sizeof(struct vstor_packet), 8)]; - struct storvsc_request_extension *request; - int ret; - - /* ASSERT(device); */ - - storDevice = MustGetStorDevice(device); - if (!storDevice) { - DPRINT_ERR(STORVSC, "unable to get stor device..." - "device being destroyed?"); - return; - } - - do { - ret = device->Driver->VmbusChannelInterface.RecvPacket(device, - packet, - ALIGN_UP(sizeof(struct vstor_packet), 8), - &bytesRecvd, &requestId); - if (ret == 0 && bytesRecvd > 0) { - DPRINT_DBG(STORVSC, "receive %d bytes - tid %llx", - bytesRecvd, requestId); - - /* ASSERT(bytesRecvd == sizeof(struct vstor_packet)); */ - - request = (struct storvsc_request_extension *) - (unsigned long)requestId; - /* ASSERT(request);c */ - - /* if (vstorPacket.Flags & SYNTHETIC_FLAG) */ - if ((request == &storDevice->InitRequest) || - (request == &storDevice->ResetRequest)) { - /* DPRINT_INFO(STORVSC, - * "reset completion - operation " - * "%u status %u", - * vstorPacket.Operation, - * vstorPacket.Status); */ - - memcpy(&request->VStorPacket, packet, - sizeof(struct vstor_packet)); - - osd_WaitEventSet(request->WaitEvent); - } else { - StorVscOnReceive(device, - (struct vstor_packet *)packet, - request); - } - } else { - /* DPRINT_DBG(STORVSC, "nothing else to read..."); */ - break; - } - } while (1); - - PutStorDevice(device); - return; -} - -static int StorVscConnectToVsp(struct hv_device *Device) -{ - struct vmstorage_channel_properties props; - struct storvsc_driver_object *storDriver; - int ret; - - storDriver = (struct storvsc_driver_object *)Device->Driver; - memset(&props, 0, sizeof(struct vmstorage_channel_properties)); - - /* Open the channel */ - ret = Device->Driver->VmbusChannelInterface.Open(Device, - storDriver->RingBufferSize, - storDriver->RingBufferSize, - (void *)&props, - sizeof(struct vmstorage_channel_properties), - StorVscOnChannelCallback, - Device); - - DPRINT_DBG(STORVSC, "storage props: path id %d, tgt id %d, max xfer %d", - props.PathId, props.TargetId, props.MaxTransferBytes); - - if (ret != 0) { - DPRINT_ERR(STORVSC, "unable to open channel: %d", ret); - return -1; - } - - ret = StorVscChannelInit(Device); - - return ret; -} - -/* - * StorVscOnDeviceAdd - Callback when the device belonging to this driver is added - */ -static int StorVscOnDeviceAdd(struct hv_device *Device, void *AdditionalInfo) -{ - struct storvsc_device *storDevice; - /* struct vmstorage_channel_properties *props; */ - struct storvsc_device_info *deviceInfo; - int ret = 0; - - deviceInfo = (struct storvsc_device_info *)AdditionalInfo; - storDevice = AllocStorDevice(Device); - if (!storDevice) { - ret = -1; - goto Cleanup; - } - - /* Save the channel properties to our storvsc channel */ - /* props = (struct vmstorage_channel_properties *) - * channel->offerMsg.Offer.u.Standard.UserDefined; */ - - /* FIXME: */ - /* - * If we support more than 1 scsi channel, we need to set the - * port number here to the scsi channel but how do we get the - * scsi channel prior to the bus scan - */ - - /* storChannel->PortNumber = 0; - storChannel->PathId = props->PathId; - storChannel->TargetId = props->TargetId; */ - - storDevice->PortNumber = deviceInfo->PortNumber; - /* Send it back up */ - ret = StorVscConnectToVsp(Device); - - /* deviceInfo->PortNumber = storDevice->PortNumber; */ - deviceInfo->PathId = storDevice->PathId; - deviceInfo->TargetId = storDevice->TargetId; - - DPRINT_DBG(STORVSC, "assigned port %u, path %u target %u\n", - storDevice->PortNumber, storDevice->PathId, - storDevice->TargetId); - -Cleanup: - return ret; -} - -/* - * StorVscOnDeviceRemove - Callback when the our device is being removed - */ -static int StorVscOnDeviceRemove(struct hv_device *Device) -{ - struct storvsc_device *storDevice; - - DPRINT_INFO(STORVSC, "disabling storage device (%p)...", - Device->Extension); - - storDevice = ReleaseStorDevice(Device); - - /* - * At this point, all outbound traffic should be disable. We - * only allow inbound traffic (responses) to proceed so that - * outstanding requests can be completed. - */ - while (atomic_read(&storDevice->NumOutstandingRequests)) { - DPRINT_INFO(STORVSC, "waiting for %d requests to complete...", - atomic_read(&storDevice->NumOutstandingRequests)); - udelay(100); - } - - DPRINT_INFO(STORVSC, "removing storage device (%p)...", - Device->Extension); - - storDevice = FinalReleaseStorDevice(Device); - - DPRINT_INFO(STORVSC, "storage device (%p) safe to remove", storDevice); - - /* Close the channel */ - Device->Driver->VmbusChannelInterface.Close(Device); - - FreeStorDevice(storDevice); - return 0; -} - -int StorVscOnHostReset(struct hv_device *Device) -{ - struct storvsc_device *storDevice; - struct storvsc_request_extension *request; - struct vstor_packet *vstorPacket; - unsigned long flags; - int ret; - - DPRINT_INFO(STORVSC, "resetting host adapter..."); - - storDevice = GetStorDevice(Device); - if (!storDevice) { - DPRINT_ERR(STORVSC, "unable to get stor device..." - "device being destroyed?"); - return -1; - } - - spin_lock_irqsave(&storDevice->lock, flags); - storDevice->reset = 1; - spin_unlock_irqrestore(&storDevice->lock, flags); - - /* - * Wait for traffic in transit to complete - */ - while (atomic_read(&storDevice->NumOutstandingRequests)) - udelay(1000); - - request = &storDevice->ResetRequest; - vstorPacket = &request->VStorPacket; - - request->WaitEvent = osd_WaitEventCreate(); - if (!request->WaitEvent) { - ret = -ENOMEM; - goto Cleanup; - } - - vstorPacket->Operation = VStorOperationResetBus; - vstorPacket->Flags = REQUEST_COMPLETION_FLAG; - vstorPacket->VmSrb.PathId = storDevice->PathId; - - ret = Device->Driver->VmbusChannelInterface.SendPacket(Device, - vstorPacket, - sizeof(struct vstor_packet), - (unsigned long)&storDevice->ResetRequest, - VmbusPacketTypeDataInBand, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret != 0) { - DPRINT_ERR(STORVSC, "Unable to send reset packet %p ret %d", - vstorPacket, ret); - goto Cleanup; - } - - /* FIXME: Add a timeout */ - osd_WaitEventWait(request->WaitEvent); - - kfree(request->WaitEvent); - DPRINT_INFO(STORVSC, "host adapter reset completed"); - - /* - * At this point, all outstanding requests in the adapter - * should have been flushed out and return to us - */ - -Cleanup: - PutStorDevice(Device); - return ret; -} - -/* - * StorVscOnIORequest - Callback to initiate an I/O request - */ -static int StorVscOnIORequest(struct hv_device *Device, - struct hv_storvsc_request *Request) -{ - struct storvsc_device *storDevice; - struct storvsc_request_extension *requestExtension; - struct vstor_packet *vstorPacket; - int ret = 0; - - requestExtension = - (struct storvsc_request_extension *)Request->Extension; - vstorPacket = &requestExtension->VStorPacket; - storDevice = GetStorDevice(Device); - - DPRINT_DBG(STORVSC, "enter - Device %p, DeviceExt %p, Request %p, " - "Extension %p", Device, storDevice, Request, - requestExtension); - - DPRINT_DBG(STORVSC, "req %p len %d bus %d, target %d, lun %d cdblen %d", - Request, Request->DataBuffer.Length, Request->Bus, - Request->TargetId, Request->LunId, Request->CdbLen); - - if (!storDevice) { - DPRINT_ERR(STORVSC, "unable to get stor device..." - "device being destroyed?"); - return -2; - } - - /* print_hex_dump_bytes("", DUMP_PREFIX_NONE, Request->Cdb, - * Request->CdbLen); */ - - requestExtension->Request = Request; - requestExtension->Device = Device; - - memset(vstorPacket, 0 , sizeof(struct vstor_packet)); - - vstorPacket->Flags |= REQUEST_COMPLETION_FLAG; - - vstorPacket->VmSrb.Length = sizeof(struct vmscsi_request); - - vstorPacket->VmSrb.PortNumber = Request->Host; - vstorPacket->VmSrb.PathId = Request->Bus; - vstorPacket->VmSrb.TargetId = Request->TargetId; - vstorPacket->VmSrb.Lun = Request->LunId; - - vstorPacket->VmSrb.SenseInfoLength = SENSE_BUFFER_SIZE; - - /* Copy over the scsi command descriptor block */ - vstorPacket->VmSrb.CdbLength = Request->CdbLen; - memcpy(&vstorPacket->VmSrb.Cdb, Request->Cdb, Request->CdbLen); - - vstorPacket->VmSrb.DataIn = Request->Type; - vstorPacket->VmSrb.DataTransferLength = Request->DataBuffer.Length; - - vstorPacket->Operation = VStorOperationExecuteSRB; - - DPRINT_DBG(STORVSC, "srb - len %d port %d, path %d, target %d, " - "lun %d senselen %d cdblen %d", - vstorPacket->VmSrb.Length, - vstorPacket->VmSrb.PortNumber, - vstorPacket->VmSrb.PathId, - vstorPacket->VmSrb.TargetId, - vstorPacket->VmSrb.Lun, - vstorPacket->VmSrb.SenseInfoLength, - vstorPacket->VmSrb.CdbLength); - - if (requestExtension->Request->DataBuffer.Length) { - ret = Device->Driver->VmbusChannelInterface. - SendPacketMultiPageBuffer(Device, - &requestExtension->Request->DataBuffer, - vstorPacket, - sizeof(struct vstor_packet), - (unsigned long)requestExtension); - } else { - ret = Device->Driver->VmbusChannelInterface.SendPacket(Device, - vstorPacket, - sizeof(struct vstor_packet), - (unsigned long)requestExtension, - VmbusPacketTypeDataInBand, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - } - - if (ret != 0) { - DPRINT_DBG(STORVSC, "Unable to send packet %p ret %d", - vstorPacket, ret); - } - - atomic_inc(&storDevice->NumOutstandingRequests); - - PutStorDevice(Device); - return ret; -} - -/* - * StorVscOnCleanup - Perform any cleanup when the driver is removed - */ -static void StorVscOnCleanup(struct hv_driver *Driver) -{ -} - -/* - * StorVscInitialize - Main entry point - */ -int StorVscInitialize(struct hv_driver *Driver) -{ - struct storvsc_driver_object *storDriver; - - storDriver = (struct storvsc_driver_object *)Driver; - - DPRINT_DBG(STORVSC, "sizeof(STORVSC_REQUEST)=%zd " - "sizeof(struct storvsc_request_extension)=%zd " - "sizeof(struct vstor_packet)=%zd, " - "sizeof(struct vmscsi_request)=%zd", - sizeof(struct hv_storvsc_request), - sizeof(struct storvsc_request_extension), - sizeof(struct vstor_packet), - sizeof(struct vmscsi_request)); - - /* Make sure we are at least 2 pages since 1 page is used for control */ - /* ASSERT(storDriver->RingBufferSize >= (PAGE_SIZE << 1)); */ - - Driver->name = gDriverName; - memcpy(&Driver->deviceType, &gStorVscDeviceType, - sizeof(struct hv_guid)); - - storDriver->RequestExtSize = sizeof(struct storvsc_request_extension); - - /* - * Divide the ring buffer data size (which is 1 page less - * than the ring buffer size since that page is reserved for - * the ring buffer indices) by the max request size (which is - * VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER + struct vstor_packet + u64) - */ - storDriver->MaxOutstandingRequestsPerChannel = - ((storDriver->RingBufferSize - PAGE_SIZE) / - ALIGN_UP(MAX_MULTIPAGE_BUFFER_PACKET + - sizeof(struct vstor_packet) + sizeof(u64), - sizeof(u64))); - - DPRINT_INFO(STORVSC, "max io %u, currently %u\n", - storDriver->MaxOutstandingRequestsPerChannel, - STORVSC_MAX_IO_REQUESTS); - - /* Setup the dispatch table */ - storDriver->Base.OnDeviceAdd = StorVscOnDeviceAdd; - storDriver->Base.OnDeviceRemove = StorVscOnDeviceRemove; - storDriver->Base.OnCleanup = StorVscOnCleanup; - - storDriver->OnIORequest = StorVscOnIORequest; - - return 0; -} diff --git a/drivers/staging/hv/StorVscApi.h b/drivers/staging/hv/StorVscApi.h deleted file mode 100644 index aad90bd..0000000 --- a/drivers/staging/hv/StorVscApi.h +++ b/dev/null @@ -1,110 +0,0 @@ -/* - * - * 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> - * - */ - - -#ifndef _STORVSC_API_H_ -#define _STORVSC_API_H_ - -#include "VmbusApi.h" - -/* Defines */ -#define STORVSC_RING_BUFFER_SIZE (20*PAGE_SIZE) -#define BLKVSC_RING_BUFFER_SIZE (20*PAGE_SIZE) - -#define STORVSC_MAX_IO_REQUESTS 128 - -/* - * In Hyper-V, each port/path/target maps to 1 scsi host adapter. In - * reality, the path/target is not used (ie always set to 0) so our - * scsi host adapter essentially has 1 bus with 1 target that contains - * up to 256 luns. - */ -#define STORVSC_MAX_LUNS_PER_TARGET 64 -#define STORVSC_MAX_TARGETS 1 -#define STORVSC_MAX_CHANNELS 1 - -struct hv_storvsc_request; - -/* Matches Windows-end */ -enum storvsc_request_type{ - WRITE_TYPE, - READ_TYPE, - UNKNOWN_TYPE, -}; - -struct hv_storvsc_request { - enum storvsc_request_type Type; - u32 Host; - u32 Bus; - u32 TargetId; - u32 LunId; - u8 *Cdb; - u32 CdbLen; - u32 Status; - u32 BytesXfer; - - unsigned char *SenseBuffer; - u32 SenseBufferSize; - - void *Context; - - void (*OnIOCompletion)(struct hv_storvsc_request *Request); - - /* This points to the memory after DataBuffer */ - void *Extension; - - struct hv_multipage_buffer DataBuffer; -}; - -/* Represents the block vsc driver */ -struct storvsc_driver_object { - /* Must be the first field */ - /* Which is a bug FIXME! */ - struct hv_driver Base; - - /* Set by caller (in bytes) */ - u32 RingBufferSize; - - /* Allocate this much private extension for each I/O request */ - u32 RequestExtSize; - - /* Maximum # of requests in flight per channel/device */ - u32 MaxOutstandingRequestsPerChannel; - - /* Specific to this driver */ - int (*OnIORequest)(struct hv_device *Device, - struct hv_storvsc_request *Request); -}; - -struct storvsc_device_info { - unsigned int PortNumber; - unsigned char PathId; - unsigned char TargetId; -}; - -/* Interface */ -int StorVscInitialize(struct hv_driver *driver); -int StorVscOnHostReset(struct hv_device *Device); -int BlkVscInitialize(struct hv_driver *driver); - -#endif /* _STORVSC_API_H_ */ diff --git a/drivers/staging/hv/TODO b/drivers/staging/hv/TODO index dbfbde9..582fd4a 100644 --- a/drivers/staging/hv/TODO +++ b/drivers/staging/hv/TODO @@ -1,15 +1,10 @@ TODO: - fix remaining checkpatch warnings and errors - - use of /** when it is not a kerneldoc header - - remove RingBuffer.c to us in-kernel ringbuffer functions instead. - audit the vmbus to verify it is working properly with the driver model - - convert vmbus driver interface function pointer tables - to constant, a.k.a vmbus_ops - see if the vmbus can be merged with the other virtual busses in the kernel - audit the network driver - - use existing net_device_stats struct in network device - checking for carrier inside open is wrong, network device API confusion?? - audit the block driver diff --git a/drivers/staging/hv/VersionInfo.h b/drivers/staging/hv/VersionInfo.h deleted file mode 100644 index 35178f2..0000000 --- a/drivers/staging/hv/VersionInfo.h +++ b/dev/null @@ -1,48 +0,0 @@ -/* - * - * 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> - * - */ - -#ifndef __HV_VERSION_INFO -#define __HV_VERSION_INFO - -/* - * We use the same version numbering for all Hyper-V modules. - * - * Definition of versioning is as follows; - * - * Major Number Changes for these scenarios; - * 1. When a new version of Windows Hyper-V - * is released. - * 2. A Major change has occurred in the - * Linux IC's. - * (For example the merge for the first time - * into the kernel) Every time the Major Number - * changes, the Revision number is reset to 0. - * Minor Number Changes when new functionality is added - * to the Linux IC's that is not a bug fix. - * - * 3.1 - Added completed hv_utils driver. Shutdown/Heartbeat/Timesync - */ -#define HV_DRV_VERSION "3.1" - - -#endif diff --git a/drivers/staging/hv/Vmbus.c b/drivers/staging/hv/Vmbus.c deleted file mode 100644 index a9b031c..0000000 --- a/drivers/staging/hv/Vmbus.c +++ b/dev/null @@ -1,293 +0,0 @@ -/* - * 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 "osd.h" -#include "logging.h" -#include "VersionInfo.h" -#include "VmbusPrivate.h" - -static const char *gDriverName = "vmbus"; - -/* - * Windows vmbus does not defined this. - * We defined this to be consistent with other devices - */ -/* {c5295816-f63a-4d5f-8d1a-4daf999ca185} */ -static const struct hv_guid gVmbusDeviceType = { - .data = { - 0x16, 0x58, 0x29, 0xc5, 0x3a, 0xf6, 0x5f, 0x4d, - 0x8d, 0x1a, 0x4d, 0xaf, 0x99, 0x9c, 0xa1, 0x85 - } -}; - -/* {ac3760fc-9adf-40aa-9427-a70ed6de95c5} */ -static const struct hv_guid gVmbusDeviceId = { - .data = { - 0xfc, 0x60, 0x37, 0xac, 0xdf, 0x9a, 0xaa, 0x40, - 0x94, 0x27, 0xa7, 0x0e, 0xd6, 0xde, 0x95, 0xc5 - } -}; - -static struct hv_driver *gDriver; /* vmbus driver object */ -static struct hv_device *gDevice; /* vmbus root device */ - -/* - * VmbusGetChannelOffers - Retrieve the channel offers from the parent partition - */ -static void VmbusGetChannelOffers(void) -{ - VmbusChannelRequestOffers(); -} - -/* - * VmbusGetChannelInterface - Get the channel interface - */ -static void VmbusGetChannelInterface(struct vmbus_channel_interface *Interface) -{ - GetChannelInterface(Interface); -} - -/* - * VmbusGetChannelInfo - Get the device info for the specified device object - */ -static void VmbusGetChannelInfo(struct hv_device *DeviceObject, - struct hv_device_info *DeviceInfo) -{ - GetChannelInfo(DeviceObject, DeviceInfo); -} - -/* - * VmbusCreateChildDevice - Creates the child device on the bus that represents the channel offer - */ -struct hv_device *VmbusChildDeviceCreate(struct hv_guid *DeviceType, - struct hv_guid *DeviceInstance, - void *Context) -{ - struct vmbus_driver *vmbusDriver = (struct vmbus_driver *)gDriver; - - return vmbusDriver->OnChildDeviceCreate(DeviceType, DeviceInstance, - Context); -} - -/* - * VmbusChildDeviceAdd - Registers the child device with the vmbus - */ -int VmbusChildDeviceAdd(struct hv_device *ChildDevice) -{ - struct vmbus_driver *vmbusDriver = (struct vmbus_driver *)gDriver; - - return vmbusDriver->OnChildDeviceAdd(gDevice, ChildDevice); -} - -/* - * VmbusChildDeviceRemove Unregisters the child device from the vmbus - */ -void VmbusChildDeviceRemove(struct hv_device *ChildDevice) -{ - struct vmbus_driver *vmbusDriver = (struct vmbus_driver *)gDriver; - - vmbusDriver->OnChildDeviceRemove(ChildDevice); -} - -/* - * VmbusOnDeviceAdd - Callback when the root bus device is added - */ -static int VmbusOnDeviceAdd(struct hv_device *dev, void *AdditionalInfo) -{ - u32 *irqvector = AdditionalInfo; - int ret; - - gDevice = dev; - - memcpy(&gDevice->deviceType, &gVmbusDeviceType, sizeof(struct hv_guid)); - memcpy(&gDevice->deviceInstance, &gVmbusDeviceId, - sizeof(struct hv_guid)); - - /* strcpy(dev->name, "vmbus"); */ - /* SynIC setup... */ - on_each_cpu(HvSynicInit, (void *)irqvector, 1); - - /* Connect to VMBus in the root partition */ - ret = VmbusConnect(); - - /* VmbusSendEvent(device->localPortId+1); */ - return ret; -} - -/* - * VmbusOnDeviceRemove - Callback when the root bus device is removed - */ -static int VmbusOnDeviceRemove(struct hv_device *dev) -{ - int ret = 0; - - VmbusChannelReleaseUnattachedChannels(); - VmbusDisconnect(); - on_each_cpu(HvSynicCleanup, NULL, 1); - return ret; -} - -/* - * VmbusOnCleanup - Perform any cleanup when the driver is removed - */ -static void VmbusOnCleanup(struct hv_driver *drv) -{ - /* struct vmbus_driver *driver = (struct vmbus_driver *)drv; */ - - HvCleanup(); -} - -/* - * VmbusOnMsgDPC - DPC routine to handle messages from the hypervisior - */ -static void VmbusOnMsgDPC(struct hv_driver *drv) -{ - int cpu = smp_processor_id(); - void *page_addr = gHvContext.synICMessagePage[cpu]; - struct hv_message *msg = (struct hv_message *)page_addr + - VMBUS_MESSAGE_SINT; - struct hv_message *copied; - - while (1) { - if (msg->Header.MessageType == HvMessageTypeNone) { - /* no msg */ - break; - } else { - copied = kmemdup(msg, sizeof(*copied), GFP_ATOMIC); - if (copied == NULL) - continue; - - osd_schedule_callback(gVmbusConnection.WorkQueue, - VmbusOnChannelMessage, - (void *)copied); - } - - msg->Header.MessageType = HvMessageTypeNone; - - /* - * Make sure the write to MessageType (ie set to - * HvMessageTypeNone) happens before we read the - * MessagePending and EOMing. Otherwise, the EOMing - * will not deliver any more messages since there is - * no empty slot - */ - mb(); - - if (msg->Header.MessageFlags.MessagePending) { - /* - * This will cause message queue rescan to - * possibly deliver another msg from the - * hypervisor - */ - wrmsrl(HV_X64_MSR_EOM, 0); - } - } -} - -/* - * VmbusOnEventDPC - DPC routine to handle events from the hypervisior - */ -static void VmbusOnEventDPC(struct hv_driver *drv) -{ - /* TODO: Process any events */ - VmbusOnEvents(); -} - -/* - * VmbusOnISR - ISR routine - */ -static int VmbusOnISR(struct hv_driver *drv) -{ - int ret = 0; - int cpu = smp_processor_id(); - void *page_addr; - struct hv_message *msg; - union hv_synic_event_flags *event; - - page_addr = gHvContext.synICMessagePage[cpu]; - msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; - - /* Check if there are actual msgs to be process */ - if (msg->Header.MessageType != HvMessageTypeNone) { - DPRINT_DBG(VMBUS, "received msg type %d size %d", - msg->Header.MessageType, - msg->Header.PayloadSize); - ret |= 0x1; - } - - /* TODO: Check if there are events to be process */ - page_addr = gHvContext.synICEventPage[cpu]; - event = (union hv_synic_event_flags *)page_addr + VMBUS_MESSAGE_SINT; - - /* Since we are a child, we only need to check bit 0 */ - if (sync_test_and_clear_bit(0, (unsigned long *) &event->Flags32[0])) { - DPRINT_DBG(VMBUS, "received event %d", event->Flags32[0]); - ret |= 0x2; - } - - return ret; -} - -/* - * VmbusInitialize - Main entry point - */ -int VmbusInitialize(struct hv_driver *drv) -{ - struct vmbus_driver *driver = (struct vmbus_driver *)drv; - int ret; - - DPRINT_INFO(VMBUS, "+++++++ HV Driver version = %s +++++++", - HV_DRV_VERSION); - DPRINT_INFO(VMBUS, "+++++++ Vmbus supported version = %d +++++++", - VMBUS_REVISION_NUMBER); - DPRINT_INFO(VMBUS, "+++++++ Vmbus using SINT %d +++++++", - VMBUS_MESSAGE_SINT); - DPRINT_DBG(VMBUS, "sizeof(VMBUS_CHANNEL_PACKET_PAGE_BUFFER)=%zd, " - "sizeof(VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER)=%zd", - sizeof(struct VMBUS_CHANNEL_PACKET_PAGE_BUFFER), - sizeof(struct VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER)); - - drv->name = gDriverName; - memcpy(&drv->deviceType, &gVmbusDeviceType, sizeof(struct hv_guid)); - - /* Setup dispatch table */ - driver->Base.OnDeviceAdd = VmbusOnDeviceAdd; - driver->Base.OnDeviceRemove = VmbusOnDeviceRemove; - driver->Base.OnCleanup = VmbusOnCleanup; - driver->OnIsr = VmbusOnISR; - driver->OnMsgDpc = VmbusOnMsgDPC; - driver->OnEventDpc = VmbusOnEventDPC; - driver->GetChannelOffers = VmbusGetChannelOffers; - driver->GetChannelInterface = VmbusGetChannelInterface; - driver->GetChannelInfo = VmbusGetChannelInfo; - - /* Hypervisor initialization...setup hypercall page..etc */ - ret = HvInit(); - if (ret != 0) - DPRINT_ERR(VMBUS, "Unable to initialize the hypervisor - 0x%x", - ret); - gDriver = drv; - - return ret; -} diff --git a/drivers/staging/hv/VmbusApi.h b/drivers/staging/hv/VmbusApi.h deleted file mode 100644 index 4275be3..0000000 --- a/drivers/staging/hv/VmbusApi.h +++ b/dev/null @@ -1,193 +0,0 @@ -/* - * - * 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> - * - */ - - -#ifndef _VMBUS_API_H_ -#define _VMBUS_API_H_ - -#define MAX_PAGE_BUFFER_COUNT 16 -#define MAX_MULTIPAGE_BUFFER_COUNT 32 /* 128K */ - -#pragma pack(push, 1) - -/* Single-page buffer */ -struct hv_page_buffer { - u32 Length; - u32 Offset; - u64 Pfn; -}; - -/* Multiple-page buffer */ -struct hv_multipage_buffer { - /* Length and Offset determines the # of pfns in the array */ - u32 Length; - u32 Offset; - u64 PfnArray[MAX_MULTIPAGE_BUFFER_COUNT]; -}; - -/* 0x18 includes the proprietary packet header */ -#define MAX_PAGE_BUFFER_PACKET (0x18 + \ - (sizeof(struct hv_page_buffer) * \ - MAX_PAGE_BUFFER_COUNT)) -#define MAX_MULTIPAGE_BUFFER_PACKET (0x18 + \ - sizeof(struct hv_multipage_buffer)) - - -#pragma pack(pop) - -struct hv_driver; -struct hv_device; - -struct hv_dev_port_info { - u32 InterruptMask; - u32 ReadIndex; - u32 WriteIndex; - u32 BytesAvailToRead; - u32 BytesAvailToWrite; -}; - -struct hv_device_info { - u32 ChannelId; - u32 ChannelState; - struct hv_guid ChannelType; - struct hv_guid ChannelInstance; - - u32 MonitorId; - u32 ServerMonitorPending; - u32 ServerMonitorLatency; - u32 ServerMonitorConnectionId; - u32 ClientMonitorPending; - u32 ClientMonitorLatency; - u32 ClientMonitorConnectionId; - - struct hv_dev_port_info Inbound; - struct hv_dev_port_info Outbound; -}; - -/** - * struct vmbus_channel_interface - Contains member functions for vmbus channel - * @Open: Open the channel - * @Close: Close the channel - * @SendPacket: Send a packet over the channel - * @SendPacketPageBuffer: Send a single page buffer over the channel - * @SendPacketMultiPageBuffer: Send a multiple page buffers - * @RecvPacket: Receive packet - * @RecvPacketRaw: Receive Raw packet - * @EstablishGpadl: Set up GPADL for ringbuffer - * @TeardownGpadl: Teardown GPADL for ringbuffer - * @GetInfo: Get info about the channel - * - * This structure contains function pointer to control vmbus channel - * behavior. None of these functions is externally callable, but they - * are used for normal vmbus channel internal behavior. - * Only used by Hyper-V drivers. - */ -struct vmbus_channel_interface { - int (*Open)(struct hv_device *Device, u32 SendBufferSize, - u32 RecvRingBufferSize, void *UserData, u32 UserDataLen, - void (*ChannelCallback)(void *context), - void *Context); - void (*Close)(struct hv_device *device); - int (*SendPacket)(struct hv_device *Device, const void *Buffer, - u32 BufferLen, u64 RequestId, u32 Type, u32 Flags); - int (*SendPacketPageBuffer)(struct hv_device *dev, - struct hv_page_buffer PageBuffers[], - u32 PageCount, void *Buffer, u32 BufferLen, - u64 RequestId); - int (*SendPacketMultiPageBuffer)(struct hv_device *device, - struct hv_multipage_buffer *mpb, - void *Buffer, - u32 BufferLen, - u64 RequestId); - int (*RecvPacket)(struct hv_device *dev, void *buf, u32 buflen, - u32 *BufferActualLen, u64 *RequestId); - int (*RecvPacketRaw)(struct hv_device *dev, void *buf, u32 buflen, - u32 *BufferActualLen, u64 *RequestId); - int (*EstablishGpadl)(struct hv_device *dev, void *buf, u32 buflen, - u32 *GpadlHandle); - int (*TeardownGpadl)(struct hv_device *device, u32 GpadlHandle); - void (*GetInfo)(struct hv_device *dev, struct hv_device_info *devinfo); -}; - -/* Base driver object */ -struct hv_driver { - const char *name; - - /* the device type supported by this driver */ - struct hv_guid deviceType; - - int (*OnDeviceAdd)(struct hv_device *device, void *data); - int (*OnDeviceRemove)(struct hv_device *device); - void (*OnCleanup)(struct hv_driver *driver); - - struct vmbus_channel_interface VmbusChannelInterface; -}; - -/* Base device object */ -struct hv_device { - /* the driver for this device */ - struct hv_driver *Driver; - - char name[64]; - - /* the device type id of this device */ - struct hv_guid deviceType; - - /* the device instance id of this device */ - struct hv_guid deviceInstance; - - void *context; - - /* Device extension; */ - void *Extension; -}; - -/* Vmbus driver object */ -struct vmbus_driver { - /* !! Must be the 1st field !! */ - /* FIXME if ^, then someone is doing somthing stupid */ - struct hv_driver Base; - - /* Set by the caller */ - struct hv_device * (*OnChildDeviceCreate)(struct hv_guid *DeviceType, - struct hv_guid *DeviceInstance, - void *Context); - void (*OnChildDeviceDestroy)(struct hv_device *device); - int (*OnChildDeviceAdd)(struct hv_device *RootDevice, - struct hv_device *ChildDevice); - void (*OnChildDeviceRemove)(struct hv_device *device); - - /* Set by the callee */ - int (*OnIsr)(struct hv_driver *driver); - void (*OnMsgDpc)(struct hv_driver *driver); - void (*OnEventDpc)(struct hv_driver *driver); - void (*GetChannelOffers)(void); - - void (*GetChannelInterface)(struct vmbus_channel_interface *i); - void (*GetChannelInfo)(struct hv_device *dev, - struct hv_device_info *devinfo); -}; - -int VmbusInitialize(struct hv_driver *drv); - -#endif /* _VMBUS_API_H_ */ diff --git a/drivers/staging/hv/VmbusChannelInterface.h b/drivers/staging/hv/VmbusChannelInterface.h deleted file mode 100644 index 2674282..0000000 --- a/drivers/staging/hv/VmbusChannelInterface.h +++ b/dev/null @@ -1,89 +0,0 @@ -/* - * - * 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> - * - */ - -#ifndef __VMBUSCHANNELINTERFACE_H -#define __VMBUSCHANNELINTERFACE_H - -/* - * A revision number of vmbus that is used for ensuring both ends on a - * partition are using compatible versions. - */ -#define VMBUS_REVISION_NUMBER 13 - -/* Make maximum size of pipe payload of 16K */ -#define MAX_PIPE_DATA_PAYLOAD (sizeof(u8) * 16384) - -/* Define PipeMode values. */ -#define VMBUS_PIPE_TYPE_BYTE 0x00000000 -#define VMBUS_PIPE_TYPE_MESSAGE 0x00000004 - -/* The size of the user defined data buffer for non-pipe offers. */ -#define MAX_USER_DEFINED_BYTES 120 - -/* The size of the user defined data buffer for pipe offers. */ -#define MAX_PIPE_USER_DEFINED_BYTES 116 - -/* - * At the center of the Channel Management library is the Channel Offer. This - * struct contains the fundamental information about an offer. - */ -struct vmbus_channel_offer { - struct hv_guid InterfaceType; - struct hv_guid InterfaceInstance; - u64 InterruptLatencyIn100nsUnits; - u32 InterfaceRevision; - u32 ServerContextAreaSize; /* in bytes */ - u16 ChannelFlags; - u16 MmioMegabytes; /* in bytes * 1024 * 1024 */ - - union { - /* Non-pipes: The user has MAX_USER_DEFINED_BYTES bytes. */ - struct { - unsigned char UserDefined[MAX_USER_DEFINED_BYTES]; - } Standard; - - /* - * Pipes: - * The following sructure is an integrated pipe protocol, which - * is implemented on top of standard user-defined data. Pipe - * clients have MAX_PIPE_USER_DEFINED_BYTES left for their own - * use. - */ - struct { - u32 PipeMode; - unsigned char UserDefined[MAX_PIPE_USER_DEFINED_BYTES]; - } Pipe; - } u; - u32 Padding; -} __attribute__((packed)); - -/* Server Flags */ -#define VMBUS_CHANNEL_ENUMERATE_DEVICE_INTERFACE 1 -#define VMBUS_CHANNEL_SERVER_SUPPORTS_TRANSFER_PAGES 2 -#define VMBUS_CHANNEL_SERVER_SUPPORTS_GPADLS 4 -#define VMBUS_CHANNEL_NAMED_PIPE_MODE 0x10 -#define VMBUS_CHANNEL_LOOPBACK_OFFER 0x100 -#define VMBUS_CHANNEL_PARENT_OFFER 0x200 -#define VMBUS_CHANNEL_REQUEST_MONITORED_NOTIFICATION 0x400 - -#endif diff --git a/drivers/staging/hv/VmbusPacketFormat.h b/drivers/staging/hv/VmbusPacketFormat.h deleted file mode 100644 index f9f6b4b..0000000 --- a/drivers/staging/hv/VmbusPacketFormat.h +++ b/dev/null @@ -1,161 +0,0 @@ -/* - * - * 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> - * - */ - -#ifndef _VMBUSPACKETFORMAT_H_ -#define _VMBUSPACKETFORMAT_H_ - -struct vmpacket_descriptor { - u16 Type; - u16 DataOffset8; - u16 Length8; - u16 Flags; - u64 TransactionId; -} __attribute__((packed)); - -struct vmpacket_header { - u32 PreviousPacketStartOffset; - struct vmpacket_descriptor Descriptor; -} __attribute__((packed)); - -struct vmtransfer_page_range { - u32 ByteCount; - u32 ByteOffset; -} __attribute__((packed)); - -struct vmtransfer_page_packet_header { - struct vmpacket_descriptor d; - u16 TransferPageSetId; - bool SenderOwnsSet; - u8 Reserved; - u32 RangeCount; - struct vmtransfer_page_range Ranges[1]; -} __attribute__((packed)); - -struct vmgpadl_packet_header { - struct vmpacket_descriptor d; - u32 Gpadl; - u32 Reserved; -} __attribute__((packed)); - -struct vmadd_remove_transfer_page_set { - struct vmpacket_descriptor d; - u32 Gpadl; - u16 TransferPageSetId; - u16 Reserved; -} __attribute__((packed)); - -/* - * This structure defines a range in guest physical space that can be made to - * look virtually contiguous. - */ -struct gpa_range { - u32 ByteCount; - u32 ByteOffset; - u64 PfnArray[0]; -}; - -/* - * This is the format for an Establish Gpadl packet, which contains a handle by - * which this GPADL will be known and a set of GPA ranges associated with it. - * This can be converted to a MDL by the guest OS. If there are multiple GPA - * ranges, then the resulting MDL will be "chained," representing multiple VA - * ranges. - */ -struct vmestablish_gpadl { - struct vmpacket_descriptor d; - u32 Gpadl; - u32 RangeCount; - struct gpa_range Range[1]; -} __attribute__((packed)); - -/* - * This is the format for a Teardown Gpadl packet, which indicates that the - * GPADL handle in the Establish Gpadl packet will never be referenced again. - */ -struct vmteardown_gpadl { - struct vmpacket_descriptor d; - u32 Gpadl; - u32 Reserved; /* for alignment to a 8-byte boundary */ -} __attribute__((packed)); - -/* - * This is the format for a GPA-Direct packet, which contains a set of GPA - * ranges, in addition to commands and/or data. - */ -struct vmdata_gpa_direct { - struct vmpacket_descriptor d; - u32 Reserved; - u32 RangeCount; - struct gpa_range Range[1]; -} __attribute__((packed)); - -/* This is the format for a Additional Data Packet. */ -struct vmadditional_data { - struct vmpacket_descriptor d; - u64 TotalBytes; - u32 ByteOffset; - u32 ByteCount; - unsigned char Data[1]; -} __attribute__((packed)); - -union vmpacket_largest_possible_header { - struct vmpacket_descriptor SimpleHeader; - struct vmtransfer_page_packet_header TransferPageHeader; - struct vmgpadl_packet_header GpadlHeader; - struct vmadd_remove_transfer_page_set AddRemoveTransferPageHeader; - struct vmestablish_gpadl EstablishGpadlHeader; - struct vmteardown_gpadl TeardownGpadlHeader; - struct vmdata_gpa_direct DataGpaDirectHeader; -}; - -#define VMPACKET_DATA_START_ADDRESS(__packet) \ - (void *)(((unsigned char *)__packet) + \ - ((struct vmpacket_descriptor)__packet)->DataOffset8 * 8) - -#define VMPACKET_DATA_LENGTH(__packet) \ - ((((struct vmpacket_descriptor)__packet)->Length8 - \ - ((struct vmpacket_descriptor)__packet)->DataOffset8) * 8) - -#define VMPACKET_TRANSFER_MODE(__packet) \ - (((struct IMPACT)__packet)->Type) - -enum vmbus_packet_type { - VmbusPacketTypeInvalid = 0x0, - VmbusPacketTypeSynch = 0x1, - VmbusPacketTypeAddTransferPageSet = 0x2, - VmbusPacketTypeRemoveTransferPageSet = 0x3, - VmbusPacketTypeEstablishGpadl = 0x4, - VmbusPacketTypeTearDownGpadl = 0x5, - VmbusPacketTypeDataInBand = 0x6, - VmbusPacketTypeDataUsingTransferPages = 0x7, - VmbusPacketTypeDataUsingGpadl = 0x8, - VmbusPacketTypeDataUsingGpaDirect = 0x9, - VmbusPacketTypeCancelRequest = 0xa, - VmbusPacketTypeCompletion = 0xb, - VmbusPacketTypeDataUsingAdditionalPackets = 0xc, - VmbusPacketTypeAdditionalData = 0xd -}; - -#define VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED 1 - -#endif diff --git a/drivers/staging/hv/VmbusPrivate.h b/drivers/staging/hv/VmbusPrivate.h deleted file mode 100644 index 5a37cce..0000000 --- a/drivers/staging/hv/VmbusPrivate.h +++ b/dev/null @@ -1,135 +0,0 @@ -/* - * - * 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> - * - */ - - -#ifndef _VMBUS_PRIVATE_H_ -#define _VMBUS_PRIVATE_H_ - -#include "Hv.h" -#include "VmbusApi.h" -#include "Channel.h" -#include "ChannelMgmt.h" -#include "ChannelInterface.h" -#include "RingBuffer.h" -#include <linux/list.h> -#include <asm/sync_bitops.h> - - -/* - * Maximum channels is determined by the size of the interrupt page - * which is PAGE_SIZE. 1/2 of PAGE_SIZE is for send endpoint interrupt - * and the other is receive endpoint interrupt - */ -#define MAX_NUM_CHANNELS ((PAGE_SIZE >> 1) << 3) /* 16348 channels */ - -/* The value here must be in multiple of 32 */ -/* TODO: Need to make this configurable */ -#define MAX_NUM_CHANNELS_SUPPORTED 256 - - -enum VMBUS_CONNECT_STATE { - Disconnected, - Connecting, - Connected, - Disconnecting -}; - -#define MAX_SIZE_CHANNEL_MESSAGE HV_MESSAGE_PAYLOAD_BYTE_COUNT - -struct VMBUS_CONNECTION { - enum VMBUS_CONNECT_STATE ConnectState; - - atomic_t NextGpadlHandle; - - /* - * Represents channel interrupts. Each bit position represents a - * channel. When a channel sends an interrupt via VMBUS, it finds its - * bit in the sendInterruptPage, set it and calls Hv to generate a port - * event. The other end receives the port event and parse the - * recvInterruptPage to see which bit is set - */ - void *InterruptPage; - void *SendInterruptPage; - void *RecvInterruptPage; - - /* - * 2 pages - 1st page for parent->child notification and 2nd - * is child->parent notification - */ - void *MonitorPages; - struct list_head ChannelMsgList; - spinlock_t channelmsg_lock; - - /* List of channels */ - struct list_head ChannelList; - spinlock_t channel_lock; - - struct workqueue_struct *WorkQueue; -}; - - -struct VMBUS_MSGINFO { - /* Bookkeeping stuff */ - struct list_head MsgListEntry; - - /* Synchronize the request/response if needed */ - struct osd_waitevent *WaitEvent; - - /* The message itself */ - unsigned char Msg[0]; -}; - - -extern struct VMBUS_CONNECTION gVmbusConnection; - -/* General vmbus interface */ - -struct hv_device *VmbusChildDeviceCreate(struct hv_guid *deviceType, - struct hv_guid *deviceInstance, - void *context); - -int VmbusChildDeviceAdd(struct hv_device *Device); - -void VmbusChildDeviceRemove(struct hv_device *Device); - -/* static void */ -/* VmbusChildDeviceDestroy( */ -/* struct hv_device *); */ - -struct vmbus_channel *GetChannelFromRelId(u32 relId); - - -/* Connection interface */ - -int VmbusConnect(void); - -int VmbusDisconnect(void); - -int VmbusPostMessage(void *buffer, size_t bufSize); - -int VmbusSetEvent(u32 childRelId); - -void VmbusOnEvents(void); - - -#endif /* _VMBUS_PRIVATE_H_ */ diff --git a/drivers/staging/hv/blkvsc_drv.c b/drivers/staging/hv/blkvsc_drv.c index ddc553c..3612574 100644 --- a/drivers/staging/hv/blkvsc_drv.c +++ b/drivers/staging/hv/blkvsc_drv.c @@ -17,6 +17,7 @@ * Authors: * Haiyang Zhang <haiyangz@microsoft.com> * Hank Janssen <hjanssen@microsoft.com> + * K. Y. Srinivasan <kys@microsoft.com> */ #include <linux/init.h> #include <linux/module.h> @@ -30,11 +31,9 @@ #include <scsi/scsi_cmnd.h> #include <scsi/scsi_eh.h> #include <scsi/scsi_dbg.h> -#include "osd.h" -#include "logging.h" -#include "VersionInfo.h" -#include "vmbus.h" -#include "StorVscApi.h" + +#include "hyperv.h" +#include "hyperv_storage.h" #define BLKVSC_MINORS 64 @@ -45,6 +44,12 @@ enum blkvsc_device_type { DVD_TYPE, }; +enum blkvsc_op_type { + DO_INQUIRY, + DO_CAPACITY, + DO_FLUSH, +}; + /* * This request ties the struct request and struct * blkvsc_request/hv_storvsc_request together A struct request may be @@ -71,9 +76,6 @@ struct blkvsc_request { /* The group this request is part of. Maybe null */ struct blkvsc_request_group *group; - wait_queue_head_t wevent; - int cond; - int write; sector_t sector_start; unsigned long sector_count; @@ -83,18 +85,12 @@ struct blkvsc_request { unsigned char cmnd[MAX_COMMAND_SIZE]; struct hv_storvsc_request request; - /* - * !!!DO NOT ADD ANYTHING BELOW HERE!!! Otherwise, memory can overlap, - * because - The extension buffer falls right here and is pointed to by - * request.Extension; - * Which sounds like a horrible idea, who designed this? - */ }; /* Per device structure */ struct block_device_context { /* point back to our device context */ - struct vm_device *device_ctx; + struct hv_device *device_ctx; struct kmem_cache *request_pool; spinlock_t lock; struct gendisk *gd; @@ -105,7 +101,6 @@ struct block_device_context { unsigned int device_id_len; int num_outstanding_reqs; int shutting_down; - int media_not_present; unsigned int sector_size; sector_t capacity; unsigned int port; @@ -114,524 +109,314 @@ struct block_device_context { int users; }; -/* Per driver */ -struct blkvsc_driver_context { - /* !! These must be the first 2 fields !! */ - /* FIXME this is a bug! */ - struct driver_context drv_ctx; - struct storvsc_driver_object drv_obj; -}; +static const char *drv_name = "blkvsc"; -/* Static decl */ -static int blkvsc_probe(struct device *dev); -static int blkvsc_remove(struct device *device); -static void blkvsc_shutdown(struct device *device); +/* {32412632-86cb-44a2-9b5c-50d1417354f5} */ +static const struct hv_guid dev_type = { + .data = { + 0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, + 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5 + } +}; -static int blkvsc_open(struct block_device *bdev, fmode_t mode); -static int blkvsc_release(struct gendisk *disk, fmode_t mode); -static int blkvsc_media_changed(struct gendisk *gd); -static int blkvsc_revalidate_disk(struct gendisk *gd); -static int blkvsc_getgeo(struct block_device *bd, struct hd_geometry *hg); -static int blkvsc_ioctl(struct block_device *bd, fmode_t mode, - unsigned cmd, unsigned long argument); -static void blkvsc_request(struct request_queue *queue); +/* + * There is a circular dependency involving blkvsc_request_completion() + * and blkvsc_do_request(). + */ static void blkvsc_request_completion(struct hv_storvsc_request *request); -static int blkvsc_do_request(struct block_device_context *blkdev, - struct request *req); -static int blkvsc_submit_request(struct blkvsc_request *blkvsc_req, - void (*request_completion)(struct hv_storvsc_request *)); -static void blkvsc_init_rw(struct blkvsc_request *blkvsc_req); -static void blkvsc_cmd_completion(struct hv_storvsc_request *request); -static int blkvsc_do_inquiry(struct block_device_context *blkdev); -static int blkvsc_do_read_capacity(struct block_device_context *blkdev); -static int blkvsc_do_read_capacity16(struct block_device_context *blkdev); -static int blkvsc_do_flush(struct block_device_context *blkdev); -static int blkvsc_cancel_pending_reqs(struct block_device_context *blkdev); -static int blkvsc_do_pending_reqs(struct block_device_context *blkdev); static int blkvsc_ringbuffer_size = BLKVSC_RING_BUFFER_SIZE; + module_param(blkvsc_ringbuffer_size, int, S_IRUGO); MODULE_PARM_DESC(ring_size, "Ring buffer size (in bytes)"); -/* The one and only one */ -static struct blkvsc_driver_context g_blkvsc_drv; - -static const struct block_device_operations block_ops = { - .owner = THIS_MODULE, - .open = blkvsc_open, - .release = blkvsc_release, - .media_changed = blkvsc_media_changed, - .revalidate_disk = blkvsc_revalidate_disk, - .getgeo = blkvsc_getgeo, - .ioctl = blkvsc_ioctl, -}; - /* - * blkvsc_drv_init - BlkVsc driver initialization. + * There is a circular dependency involving blkvsc_probe() + * and block_ops. */ -static int blkvsc_drv_init(int (*drv_init)(struct hv_driver *drv)) -{ - struct storvsc_driver_object *storvsc_drv_obj = &g_blkvsc_drv.drv_obj; - struct driver_context *drv_ctx = &g_blkvsc_drv.drv_ctx; - int ret; +static int blkvsc_probe(struct hv_device *dev); - vmbus_get_interface(&storvsc_drv_obj->Base.VmbusChannelInterface); +static int blkvsc_device_add(struct hv_device *device, + void *additional_info) +{ + struct storvsc_device_info *device_info; + int ret = 0; - storvsc_drv_obj->RingBufferSize = blkvsc_ringbuffer_size; + device_info = (struct storvsc_device_info *)additional_info; - /* Callback to client driver to complete the initialization */ - drv_init(&storvsc_drv_obj->Base); + device_info->ring_buffer_size = blkvsc_ringbuffer_size; - drv_ctx->driver.name = storvsc_drv_obj->Base.name; - memcpy(&drv_ctx->class_id, &storvsc_drv_obj->Base.deviceType, - sizeof(struct hv_guid)); + ret = storvsc_dev_add(device, additional_info); + if (ret != 0) + return ret; - drv_ctx->probe = blkvsc_probe; - drv_ctx->remove = blkvsc_remove; - drv_ctx->shutdown = blkvsc_shutdown; + /* + * We need to use the device instance guid to set the path and target + * id. For IDE devices, the device instance id is formatted as + * <bus id> * - <device id> - 8899 - 000000000000. + */ + device_info->path_id = device->dev_instance.data[3] << 24 | + device->dev_instance.data[2] << 16 | + device->dev_instance.data[1] << 8 | + device->dev_instance.data[0]; - /* The driver belongs to vmbus */ - ret = vmbus_child_driver_register(drv_ctx); + device_info->target_id = device->dev_instance.data[5] << 8 | + device->dev_instance.data[4]; return ret; } -static int blkvsc_drv_exit_cb(struct device *dev, void *data) -{ - struct device **curr = (struct device **)data; - *curr = dev; - return 1; /* stop iterating */ -} - -static void blkvsc_drv_exit(void) +static int blkvsc_submit_request(struct blkvsc_request *blkvsc_req, + void (*request_completion)(struct hv_storvsc_request *)) { - struct storvsc_driver_object *storvsc_drv_obj = &g_blkvsc_drv.drv_obj; - struct driver_context *drv_ctx = &g_blkvsc_drv.drv_ctx; - struct device *current_dev; + struct block_device_context *blkdev = blkvsc_req->dev; + struct hv_storvsc_request *storvsc_req; + struct vmscsi_request *vm_srb; int ret; - while (1) { - current_dev = NULL; - - /* Get the device */ - ret = driver_for_each_device(&drv_ctx->driver, NULL, - (void *) ¤t_dev, - blkvsc_drv_exit_cb); - - if (ret) - DPRINT_WARN(BLKVSC_DRV, - "driver_for_each_device returned %d", ret); - - if (current_dev == NULL) - break; + storvsc_req = &blkvsc_req->request; + vm_srb = &storvsc_req->vstor_packet.vm_srb; - /* Initiate removal from the top-down */ - device_unregister(current_dev); - } + vm_srb->data_in = blkvsc_req->write ? WRITE_TYPE : READ_TYPE; - if (storvsc_drv_obj->Base.OnCleanup) - storvsc_drv_obj->Base.OnCleanup(&storvsc_drv_obj->Base); + storvsc_req->on_io_completion = request_completion; + storvsc_req->context = blkvsc_req; - vmbus_child_driver_unregister(drv_ctx); + vm_srb->port_number = blkdev->port; + vm_srb->path_id = blkdev->path; + vm_srb->target_id = blkdev->target; + vm_srb->lun = 0; /* this is not really used at all */ - return; -} + vm_srb->cdb_length = blkvsc_req->cmd_len; -/* - * blkvsc_probe - Add a new device for this driver - */ -static int blkvsc_probe(struct device *device) -{ - struct driver_context *driver_ctx = - driver_to_driver_context(device->driver); - struct blkvsc_driver_context *blkvsc_drv_ctx = - (struct blkvsc_driver_context *)driver_ctx; - struct storvsc_driver_object *storvsc_drv_obj = - &blkvsc_drv_ctx->drv_obj; - struct vm_device *device_ctx = device_to_vm_device(device); - struct hv_device *device_obj = &device_ctx->device_obj; - - struct block_device_context *blkdev = NULL; - struct storvsc_device_info device_info; - int major = 0; - int devnum = 0; - int ret = 0; - static int ide0_registered; - static int ide1_registered; + memcpy(vm_srb->cdb, blkvsc_req->cmnd, vm_srb->cdb_length); - DPRINT_DBG(BLKVSC_DRV, "blkvsc_probe - enter"); + storvsc_req->sense_buffer = blkvsc_req->sense_buffer; - if (!storvsc_drv_obj->Base.OnDeviceAdd) { - DPRINT_ERR(BLKVSC_DRV, "OnDeviceAdd() not set"); - ret = -1; - goto Cleanup; - } + ret = storvsc_do_io(blkdev->device_ctx, + &blkvsc_req->request); + if (ret == 0) + blkdev->num_outstanding_reqs++; - blkdev = kzalloc(sizeof(struct block_device_context), GFP_KERNEL); - if (!blkdev) { - ret = -ENOMEM; - goto Cleanup; - } + return ret; +} - INIT_LIST_HEAD(&blkdev->pending_list); - /* Initialize what we can here */ - spin_lock_init(&blkdev->lock); +static int blkvsc_open(struct block_device *bdev, fmode_t mode) +{ + struct block_device_context *blkdev = bdev->bd_disk->private_data; + unsigned long flags; - /* ASSERT(sizeof(struct blkvsc_request_group) <= */ - /* sizeof(struct blkvsc_request)); */ + spin_lock_irqsave(&blkdev->lock, flags); - blkdev->request_pool = kmem_cache_create(dev_name(&device_ctx->device), - sizeof(struct blkvsc_request) + - storvsc_drv_obj->RequestExtSize, 0, - SLAB_HWCACHE_ALIGN, NULL); - if (!blkdev->request_pool) { - ret = -ENOMEM; - goto Cleanup; - } + blkdev->users++; + spin_unlock_irqrestore(&blkdev->lock, flags); - /* Call to the vsc driver to add the device */ - ret = storvsc_drv_obj->Base.OnDeviceAdd(device_obj, &device_info); - if (ret != 0) { - DPRINT_ERR(BLKVSC_DRV, "unable to add blkvsc device"); - goto Cleanup; - } + return 0; +} - blkdev->device_ctx = device_ctx; - /* this identified the device 0 or 1 */ - blkdev->target = device_info.TargetId; - /* this identified the ide ctrl 0 or 1 */ - blkdev->path = device_info.PathId; - - dev_set_drvdata(device, blkdev); - - /* Calculate the major and device num */ - if (blkdev->path == 0) { - major = IDE0_MAJOR; - devnum = blkdev->path + blkdev->target; /* 0 or 1 */ - - if (!ide0_registered) { - ret = register_blkdev(major, "ide"); - if (ret != 0) { - DPRINT_ERR(BLKVSC_DRV, - "register_blkdev() failed! ret %d", - ret); - goto Remove; - } - ide0_registered = 1; - } - } else if (blkdev->path == 1) { - major = IDE1_MAJOR; - devnum = blkdev->path + blkdev->target + 1; /* 2 or 3 */ - - if (!ide1_registered) { - ret = register_blkdev(major, "ide"); - if (ret != 0) { - DPRINT_ERR(BLKVSC_DRV, - "register_blkdev() failed! ret %d", - ret); - goto Remove; - } - - ide1_registered = 1; - } - } else { - DPRINT_ERR(BLKVSC_DRV, "invalid pathid"); - ret = -1; - goto Cleanup; - } +static int blkvsc_getgeo(struct block_device *bd, struct hd_geometry *hg) +{ + sector_t nsect = get_capacity(bd->bd_disk); + sector_t cylinders = nsect; - DPRINT_INFO(BLKVSC_DRV, "blkvsc registered for major %d!!", major); + /* + * We are making up these values; let us keep it simple. + */ + hg->heads = 0xff; + hg->sectors = 0x3f; + sector_div(cylinders, hg->heads * hg->sectors); + hg->cylinders = cylinders; + if ((sector_t)(hg->cylinders + 1) * hg->heads * hg->sectors < nsect) + hg->cylinders = 0xffff; + return 0; - blkdev->gd = alloc_disk(BLKVSC_MINORS); - if (!blkdev->gd) { - DPRINT_ERR(BLKVSC_DRV, "register_blkdev() failed! ret %d", ret); - ret = -1; - goto Cleanup; - } +} - blkdev->gd->queue = blk_init_queue(blkvsc_request, &blkdev->lock); - blk_queue_max_segment_size(blkdev->gd->queue, PAGE_SIZE); - blk_queue_max_segments(blkdev->gd->queue, MAX_MULTIPAGE_BUFFER_COUNT); - blk_queue_segment_boundary(blkdev->gd->queue, PAGE_SIZE-1); - blk_queue_bounce_limit(blkdev->gd->queue, BLK_BOUNCE_ANY); - blk_queue_dma_alignment(blkdev->gd->queue, 511); +static void blkvsc_init_rw(struct blkvsc_request *blkvsc_req) +{ - blkdev->gd->major = major; - if (devnum == 1 || devnum == 3) - blkdev->gd->first_minor = BLKVSC_MINORS; - else - blkdev->gd->first_minor = 0; - blkdev->gd->fops = &block_ops; - blkdev->gd->private_data = blkdev; - blkdev->gd->driverfs_dev = &(blkdev->device_ctx->device); - sprintf(blkdev->gd->disk_name, "hd%c", 'a' + devnum); + blkvsc_req->cmd_len = 16; - blkvsc_do_inquiry(blkdev); - if (blkdev->device_type == DVD_TYPE) { - set_disk_ro(blkdev->gd, 1); - blkdev->gd->flags |= GENHD_FL_REMOVABLE; - blkvsc_do_read_capacity(blkdev); + if (rq_data_dir(blkvsc_req->req)) { + blkvsc_req->write = 1; + blkvsc_req->cmnd[0] = WRITE_16; } else { - blkvsc_do_read_capacity16(blkdev); + blkvsc_req->write = 0; + blkvsc_req->cmnd[0] = READ_16; } - set_capacity(blkdev->gd, blkdev->capacity * (blkdev->sector_size/512)); - blk_queue_logical_block_size(blkdev->gd->queue, blkdev->sector_size); - /* go! */ - add_disk(blkdev->gd); + blkvsc_req->cmnd[1] |= + (blkvsc_req->req->cmd_flags & REQ_FUA) ? 0x8 : 0; - DPRINT_INFO(BLKVSC_DRV, "%s added!! capacity %lu sector_size %d", - blkdev->gd->disk_name, (unsigned long)blkdev->capacity, - blkdev->sector_size); + *(unsigned long long *)&blkvsc_req->cmnd[2] = + cpu_to_be64(blkvsc_req->sector_start); + *(unsigned int *)&blkvsc_req->cmnd[10] = + cpu_to_be32(blkvsc_req->sector_count); +} - return ret; -Remove: - storvsc_drv_obj->Base.OnDeviceRemove(device_obj); +static int blkvsc_ioctl(struct block_device *bd, fmode_t mode, + unsigned cmd, unsigned long arg) +{ + struct block_device_context *blkdev = bd->bd_disk->private_data; + int ret = 0; -Cleanup: - if (blkdev) { - if (blkdev->request_pool) { - kmem_cache_destroy(blkdev->request_pool); - blkdev->request_pool = NULL; - } - kfree(blkdev); - blkdev = NULL; + switch (cmd) { + case HDIO_GET_IDENTITY: + if (copy_to_user((void __user *)arg, blkdev->device_id, + blkdev->device_id_len)) + ret = -EFAULT; + break; + default: + ret = -EINVAL; + break; } return ret; } -static void blkvsc_shutdown(struct device *device) +static void blkvsc_cmd_completion(struct hv_storvsc_request *request) { - struct block_device_context *blkdev = dev_get_drvdata(device); + struct blkvsc_request *blkvsc_req = + (struct blkvsc_request *)request->context; + struct block_device_context *blkdev = + (struct block_device_context *)blkvsc_req->dev; + struct scsi_sense_hdr sense_hdr; + struct vmscsi_request *vm_srb; unsigned long flags; - if (!blkdev) - return; - DPRINT_DBG(BLKVSC_DRV, "blkvsc_shutdown - users %d disk %s\n", - blkdev->users, blkdev->gd->disk_name); + vm_srb = &blkvsc_req->request.vstor_packet.vm_srb; spin_lock_irqsave(&blkdev->lock, flags); - - blkdev->shutting_down = 1; - - blk_stop_queue(blkdev->gd->queue); - + blkdev->num_outstanding_reqs--; spin_unlock_irqrestore(&blkdev->lock, flags); - while (blkdev->num_outstanding_reqs) { - DPRINT_INFO(STORVSC, "waiting for %d requests to complete...", - blkdev->num_outstanding_reqs); - udelay(100); - } - - blkvsc_do_flush(blkdev); - - spin_lock_irqsave(&blkdev->lock, flags); - - blkvsc_cancel_pending_reqs(blkdev); + if (vm_srb->scsi_status) + if (scsi_normalize_sense(blkvsc_req->sense_buffer, + SCSI_SENSE_BUFFERSIZE, &sense_hdr)) + scsi_print_sense_hdr("blkvsc", &sense_hdr); - spin_unlock_irqrestore(&blkdev->lock, flags); + complete(&blkvsc_req->request.wait_event); } -static int blkvsc_do_flush(struct block_device_context *blkdev) -{ - struct blkvsc_request *blkvsc_req; - DPRINT_DBG(BLKVSC_DRV, "blkvsc_do_flush()\n"); - - if (blkdev->device_type != HARDDISK_TYPE) - return 0; - - blkvsc_req = kmem_cache_alloc(blkdev->request_pool, GFP_KERNEL); - if (!blkvsc_req) - return -ENOMEM; - - memset(blkvsc_req, 0, sizeof(struct blkvsc_request)); - init_waitqueue_head(&blkvsc_req->wevent); - blkvsc_req->dev = blkdev; - blkvsc_req->req = NULL; - blkvsc_req->write = 0; - - blkvsc_req->request.DataBuffer.PfnArray[0] = 0; - blkvsc_req->request.DataBuffer.Offset = 0; - blkvsc_req->request.DataBuffer.Length = 0; - - blkvsc_req->cmnd[0] = SYNCHRONIZE_CACHE; - blkvsc_req->cmd_len = 10; - - /* - * Set this here since the completion routine may be invoked and - * completed before we return - */ - blkvsc_req->cond = 0; - blkvsc_submit_request(blkvsc_req, blkvsc_cmd_completion); - - wait_event_interruptible(blkvsc_req->wevent, blkvsc_req->cond); - - kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req); - - return 0; -} - -/* Do a scsi INQUIRY cmd here to get the device type (ie disk or dvd) */ -static int blkvsc_do_inquiry(struct block_device_context *blkdev) +static int blkvsc_do_operation(struct block_device_context *blkdev, + enum blkvsc_op_type op) { struct blkvsc_request *blkvsc_req; struct page *page_buf; unsigned char *buf; unsigned char device_type; + struct scsi_sense_hdr sense_hdr; + struct vmscsi_request *vm_srb; + unsigned long flags; - DPRINT_DBG(BLKVSC_DRV, "blkvsc_do_inquiry()\n"); + int ret = 0; - blkvsc_req = kmem_cache_alloc(blkdev->request_pool, GFP_KERNEL); + blkvsc_req = kmem_cache_zalloc(blkdev->request_pool, GFP_KERNEL); if (!blkvsc_req) return -ENOMEM; - memset(blkvsc_req, 0, sizeof(struct blkvsc_request)); page_buf = alloc_page(GFP_KERNEL); if (!page_buf) { kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req); return -ENOMEM; } - init_waitqueue_head(&blkvsc_req->wevent); + vm_srb = &blkvsc_req->request.vstor_packet.vm_srb; + init_completion(&blkvsc_req->request.wait_event); blkvsc_req->dev = blkdev; blkvsc_req->req = NULL; blkvsc_req->write = 0; - blkvsc_req->request.DataBuffer.PfnArray[0] = page_to_pfn(page_buf); - blkvsc_req->request.DataBuffer.Offset = 0; - blkvsc_req->request.DataBuffer.Length = 64; - - blkvsc_req->cmnd[0] = INQUIRY; - blkvsc_req->cmnd[1] = 0x1; /* Get product data */ - blkvsc_req->cmnd[2] = 0x83; /* mode page 83 */ - blkvsc_req->cmnd[4] = 64; - blkvsc_req->cmd_len = 6; - - /* - * Set this here since the completion routine may be invoked and - * completed before we return - */ - blkvsc_req->cond = 0; - - blkvsc_submit_request(blkvsc_req, blkvsc_cmd_completion); - - DPRINT_DBG(BLKVSC_DRV, "waiting %p to complete - cond %d\n", - blkvsc_req, blkvsc_req->cond); - - wait_event_interruptible(blkvsc_req->wevent, blkvsc_req->cond); + blkvsc_req->request.data_buffer.pfn_array[0] = + page_to_pfn(page_buf); + blkvsc_req->request.data_buffer.offset = 0; + + switch (op) { + case DO_INQUIRY: + blkvsc_req->cmnd[0] = INQUIRY; + blkvsc_req->cmnd[1] = 0x1; /* Get product data */ + blkvsc_req->cmnd[2] = 0x83; /* mode page 83 */ + blkvsc_req->cmnd[4] = 64; + blkvsc_req->cmd_len = 6; + blkvsc_req->request.data_buffer.len = 64; + break; - buf = kmap(page_buf); + case DO_CAPACITY: + blkdev->sector_size = 0; + blkdev->capacity = 0; - /* print_hex_dump_bytes("", DUMP_PREFIX_NONE, buf, 64); */ - /* be to le */ - device_type = buf[0] & 0x1F; + blkvsc_req->cmnd[0] = READ_CAPACITY; + blkvsc_req->cmd_len = 16; + blkvsc_req->request.data_buffer.len = 8; + break; - if (device_type == 0x0) { - blkdev->device_type = HARDDISK_TYPE; - } else if (device_type == 0x5) { - blkdev->device_type = DVD_TYPE; - } else { - /* TODO: this is currently unsupported device type */ - blkdev->device_type = UNKNOWN_DEV_TYPE; + case DO_FLUSH: + blkvsc_req->cmnd[0] = SYNCHRONIZE_CACHE; + blkvsc_req->cmd_len = 10; + blkvsc_req->request.data_buffer.pfn_array[0] = 0; + blkvsc_req->request.data_buffer.len = 0; + break; + default: + ret = -EINVAL; + goto cleanup; } - DPRINT_DBG(BLKVSC_DRV, "device type %d\n", device_type); - - blkdev->device_id_len = buf[7]; - if (blkdev->device_id_len > 64) - blkdev->device_id_len = 64; - - memcpy(blkdev->device_id, &buf[8], blkdev->device_id_len); - /* printk_hex_dump_bytes("", DUMP_PREFIX_NONE, blkdev->device_id, - * blkdev->device_id_len); */ - - kunmap(page_buf); - - __free_page(page_buf); - - kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req); - - return 0; -} - -/* Do a scsi READ_CAPACITY cmd here to get the size of the disk */ -static int blkvsc_do_read_capacity(struct block_device_context *blkdev) -{ - struct blkvsc_request *blkvsc_req; - struct page *page_buf; - unsigned char *buf; - struct scsi_sense_hdr sense_hdr; - - DPRINT_DBG(BLKVSC_DRV, "blkvsc_do_read_capacity()\n"); + spin_lock_irqsave(&blkdev->lock, flags); + blkvsc_submit_request(blkvsc_req, blkvsc_cmd_completion); + spin_unlock_irqrestore(&blkdev->lock, flags); - blkdev->sector_size = 0; - blkdev->capacity = 0; - blkdev->media_not_present = 0; /* assume a disk is present */ + wait_for_completion_interruptible(&blkvsc_req->request.wait_event); - blkvsc_req = kmem_cache_alloc(blkdev->request_pool, GFP_KERNEL); - if (!blkvsc_req) - return -ENOMEM; + /* check error */ + if (vm_srb->scsi_status) { + scsi_normalize_sense(blkvsc_req->sense_buffer, + SCSI_SENSE_BUFFERSIZE, &sense_hdr); - memset(blkvsc_req, 0, sizeof(struct blkvsc_request)); - page_buf = alloc_page(GFP_KERNEL); - if (!page_buf) { - kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req); - return -ENOMEM; + return 0; } - init_waitqueue_head(&blkvsc_req->wevent); - blkvsc_req->dev = blkdev; - blkvsc_req->req = NULL; - blkvsc_req->write = 0; + buf = kmap(page_buf); - blkvsc_req->request.DataBuffer.PfnArray[0] = page_to_pfn(page_buf); - blkvsc_req->request.DataBuffer.Offset = 0; - blkvsc_req->request.DataBuffer.Length = 8; + switch (op) { + case DO_INQUIRY: + device_type = buf[0] & 0x1F; - blkvsc_req->cmnd[0] = READ_CAPACITY; - blkvsc_req->cmd_len = 16; + if (device_type == 0x0) + blkdev->device_type = HARDDISK_TYPE; + else + blkdev->device_type = UNKNOWN_DEV_TYPE; - /* - * Set this here since the completion routine may be invoked - * and completed before we return - */ - blkvsc_req->cond = 0; + blkdev->device_id_len = buf[7]; + if (blkdev->device_id_len > 64) + blkdev->device_id_len = 64; - blkvsc_submit_request(blkvsc_req, blkvsc_cmd_completion); - - DPRINT_DBG(BLKVSC_DRV, "waiting %p to complete - cond %d\n", - blkvsc_req, blkvsc_req->cond); + memcpy(blkdev->device_id, &buf[8], blkdev->device_id_len); + break; - wait_event_interruptible(blkvsc_req->wevent, blkvsc_req->cond); + case DO_CAPACITY: + /* be to le */ + blkdev->capacity = + ((buf[0] << 24) | (buf[1] << 16) | + (buf[2] << 8) | buf[3]) + 1; - /* check error */ - if (blkvsc_req->request.Status) { - scsi_normalize_sense(blkvsc_req->sense_buffer, - SCSI_SENSE_BUFFERSIZE, &sense_hdr); + blkdev->sector_size = + (buf[4] << 24) | (buf[5] << 16) | + (buf[6] << 8) | buf[7]; + break; + default: + break; - if (sense_hdr.asc == 0x3A) { - /* Medium not present */ - blkdev->media_not_present = 1; - } - return 0; } - buf = kmap(page_buf); - /* be to le */ - blkdev->capacity = ((buf[0] << 24) | (buf[1] << 16) | - (buf[2] << 8) | buf[3]) + 1; - blkdev->sector_size = (buf[4] << 24) | (buf[5] << 16) | - (buf[6] << 8) | buf[7]; +cleanup: kunmap(page_buf); @@ -639,122 +424,86 @@ static int blkvsc_do_read_capacity(struct block_device_context *blkdev) kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req); - return 0; + return ret; } -static int blkvsc_do_read_capacity16(struct block_device_context *blkdev) -{ - struct blkvsc_request *blkvsc_req; - struct page *page_buf; - unsigned char *buf; - struct scsi_sense_hdr sense_hdr; - - DPRINT_DBG(BLKVSC_DRV, "blkvsc_do_read_capacity16()\n"); - - blkdev->sector_size = 0; - blkdev->capacity = 0; - blkdev->media_not_present = 0; /* assume a disk is present */ - - blkvsc_req = kmem_cache_alloc(blkdev->request_pool, GFP_KERNEL); - if (!blkvsc_req) - return -ENOMEM; - memset(blkvsc_req, 0, sizeof(struct blkvsc_request)); - page_buf = alloc_page(GFP_KERNEL); - if (!page_buf) { - kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req); - return -ENOMEM; - } +static int blkvsc_cancel_pending_reqs(struct block_device_context *blkdev) +{ + struct blkvsc_request *pend_req, *tmp; + struct blkvsc_request *comp_req, *tmp2; + struct vmscsi_request *vm_srb; - init_waitqueue_head(&blkvsc_req->wevent); - blkvsc_req->dev = blkdev; - blkvsc_req->req = NULL; - blkvsc_req->write = 0; + int ret = 0; - blkvsc_req->request.DataBuffer.PfnArray[0] = page_to_pfn(page_buf); - blkvsc_req->request.DataBuffer.Offset = 0; - blkvsc_req->request.DataBuffer.Length = 12; - blkvsc_req->cmnd[0] = 0x9E; /* READ_CAPACITY16; */ - blkvsc_req->cmd_len = 16; + /* Flush the pending list first */ + list_for_each_entry_safe(pend_req, tmp, &blkdev->pending_list, + pend_entry) { + /* + * The pend_req could be part of a partially completed + * request. If so, complete those req first until we + * hit the pend_req + */ + list_for_each_entry_safe(comp_req, tmp2, + &pend_req->group->blkvsc_req_list, + req_entry) { - /* - * Set this here since the completion routine may be invoked - * and completed before we return - */ - blkvsc_req->cond = 0; + if (comp_req == pend_req) + break; - blkvsc_submit_request(blkvsc_req, blkvsc_cmd_completion); + list_del(&comp_req->req_entry); - DPRINT_DBG(BLKVSC_DRV, "waiting %p to complete - cond %d\n", - blkvsc_req, blkvsc_req->cond); + if (comp_req->req) { + vm_srb = + &comp_req->request.vstor_packet. + vm_srb; + ret = __blk_end_request(comp_req->req, + (!vm_srb->scsi_status ? 0 : -EIO), + comp_req->sector_count * + blkdev->sector_size); - wait_event_interruptible(blkvsc_req->wevent, blkvsc_req->cond); + /* FIXME: shouldn't this do more than return? */ + if (ret) + goto out; + } - /* check error */ - if (blkvsc_req->request.Status) { - scsi_normalize_sense(blkvsc_req->sense_buffer, - SCSI_SENSE_BUFFERSIZE, &sense_hdr); - if (sense_hdr.asc == 0x3A) { - /* Medium not present */ - blkdev->media_not_present = 1; + kmem_cache_free(blkdev->request_pool, comp_req); } - return 0; - } - buf = kmap(page_buf); - - /* be to le */ - blkdev->capacity = be64_to_cpu(*(unsigned long long *) &buf[0]) + 1; - blkdev->sector_size = be32_to_cpu(*(unsigned int *)&buf[8]); -#if 0 - blkdev->capacity = ((buf[0] << 24) | (buf[1] << 16) | - (buf[2] << 8) | buf[3]) + 1; - blkdev->sector_size = (buf[4] << 24) | (buf[5] << 16) | - (buf[6] << 8) | buf[7]; -#endif + list_del(&pend_req->pend_entry); - kunmap(page_buf); + list_del(&pend_req->req_entry); - __free_page(page_buf); + if (comp_req->req) { + if (!__blk_end_request(pend_req->req, -EIO, + pend_req->sector_count * + blkdev->sector_size)) { + /* + * All the sectors have been xferred ie the + * request is done + */ + kmem_cache_free(blkdev->request_pool, + pend_req->group); + } + } - kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req); + kmem_cache_free(blkdev->request_pool, pend_req); + } - return 0; +out: + return ret; } + /* * blkvsc_remove() - Callback when our device is removed */ -static int blkvsc_remove(struct device *device) +static int blkvsc_remove(struct hv_device *dev) { - struct driver_context *driver_ctx = - driver_to_driver_context(device->driver); - struct blkvsc_driver_context *blkvsc_drv_ctx = - (struct blkvsc_driver_context *)driver_ctx; - struct storvsc_driver_object *storvsc_drv_obj = - &blkvsc_drv_ctx->drv_obj; - struct vm_device *device_ctx = device_to_vm_device(device); - struct hv_device *device_obj = &device_ctx->device_obj; - struct block_device_context *blkdev = dev_get_drvdata(device); + struct block_device_context *blkdev = dev_get_drvdata(&dev->device); unsigned long flags; - int ret; - - DPRINT_DBG(BLKVSC_DRV, "blkvsc_remove()\n"); - if (!storvsc_drv_obj->Base.OnDeviceRemove) - return -1; - - /* - * Call to the vsc driver to let it know that the device is being - * removed - */ - ret = storvsc_drv_obj->Base.OnDeviceRemove(device_obj); - if (ret != 0) { - /* TODO: */ - DPRINT_ERR(BLKVSC_DRV, - "unable to remove blkvsc device (ret %d)", ret); - } /* Get to a known state */ spin_lock_irqsave(&blkdev->lock, flags); @@ -763,148 +512,80 @@ static int blkvsc_remove(struct device *device) blk_stop_queue(blkdev->gd->queue); - spin_unlock_irqrestore(&blkdev->lock, flags); - - while (blkdev->num_outstanding_reqs) { - DPRINT_INFO(STORVSC, "waiting for %d requests to complete...", - blkdev->num_outstanding_reqs); - udelay(100); - } - - blkvsc_do_flush(blkdev); - - spin_lock_irqsave(&blkdev->lock, flags); - blkvsc_cancel_pending_reqs(blkdev); spin_unlock_irqrestore(&blkdev->lock, flags); - blk_cleanup_queue(blkdev->gd->queue); + blkvsc_do_operation(blkdev, DO_FLUSH); - del_gendisk(blkdev->gd); + if (blkdev->users == 0) { + del_gendisk(blkdev->gd); + put_disk(blkdev->gd); + blk_cleanup_queue(blkdev->gd->queue); - kmem_cache_destroy(blkdev->request_pool); + storvsc_dev_remove(blkdev->device_ctx); - kfree(blkdev); + kmem_cache_destroy(blkdev->request_pool); + kfree(blkdev); + } - return ret; + return 0; } -static void blkvsc_init_rw(struct blkvsc_request *blkvsc_req) +static void blkvsc_shutdown(struct hv_device *dev) { - /* ASSERT(blkvsc_req->req); */ - /* ASSERT(blkvsc_req->sector_count <= (MAX_MULTIPAGE_BUFFER_COUNT*8)); */ + struct block_device_context *blkdev = dev_get_drvdata(&dev->device); + unsigned long flags; - blkvsc_req->cmd_len = 16; + if (!blkdev) + return; - if (blkvsc_req->sector_start > 0xffffffff) { - if (rq_data_dir(blkvsc_req->req)) { - blkvsc_req->write = 1; - blkvsc_req->cmnd[0] = WRITE_16; - } else { - blkvsc_req->write = 0; - blkvsc_req->cmnd[0] = READ_16; - } + spin_lock_irqsave(&blkdev->lock, flags); - blkvsc_req->cmnd[1] |= - (blkvsc_req->req->cmd_flags & REQ_FUA) ? 0x8 : 0; - - *(unsigned long long *)&blkvsc_req->cmnd[2] = - cpu_to_be64(blkvsc_req->sector_start); - *(unsigned int *)&blkvsc_req->cmnd[10] = - cpu_to_be32(blkvsc_req->sector_count); - } else if ((blkvsc_req->sector_count > 0xff) || - (blkvsc_req->sector_start > 0x1fffff)) { - if (rq_data_dir(blkvsc_req->req)) { - blkvsc_req->write = 1; - blkvsc_req->cmnd[0] = WRITE_10; - } else { - blkvsc_req->write = 0; - blkvsc_req->cmnd[0] = READ_10; - } + blkdev->shutting_down = 1; - blkvsc_req->cmnd[1] |= - (blkvsc_req->req->cmd_flags & REQ_FUA) ? 0x8 : 0; + blk_stop_queue(blkdev->gd->queue); - *(unsigned int *)&blkvsc_req->cmnd[2] = - cpu_to_be32(blkvsc_req->sector_start); - *(unsigned short *)&blkvsc_req->cmnd[7] = - cpu_to_be16(blkvsc_req->sector_count); - } else { - if (rq_data_dir(blkvsc_req->req)) { - blkvsc_req->write = 1; - blkvsc_req->cmnd[0] = WRITE_6; - } else { - blkvsc_req->write = 0; - blkvsc_req->cmnd[0] = READ_6; - } + blkvsc_cancel_pending_reqs(blkdev); - *(unsigned int *)&blkvsc_req->cmnd[1] = - cpu_to_be32(blkvsc_req->sector_start) >> 8; - blkvsc_req->cmnd[1] &= 0x1f; - blkvsc_req->cmnd[4] = (unsigned char)blkvsc_req->sector_count; - } -} + spin_unlock_irqrestore(&blkdev->lock, flags); -static int blkvsc_submit_request(struct blkvsc_request *blkvsc_req, - void (*request_completion)(struct hv_storvsc_request *)) -{ - struct block_device_context *blkdev = blkvsc_req->dev; - struct vm_device *device_ctx = blkdev->device_ctx; - struct driver_context *driver_ctx = - driver_to_driver_context(device_ctx->device.driver); - struct blkvsc_driver_context *blkvsc_drv_ctx = - (struct blkvsc_driver_context *)driver_ctx; - struct storvsc_driver_object *storvsc_drv_obj = - &blkvsc_drv_ctx->drv_obj; - struct hv_storvsc_request *storvsc_req; - int ret; + blkvsc_do_operation(blkdev, DO_FLUSH); - DPRINT_DBG(BLKVSC_DRV, "blkvsc_submit_request() - " - "req %p type %s start_sector %lu count %ld offset %d " - "len %d\n", blkvsc_req, - (blkvsc_req->write) ? "WRITE" : "READ", - (unsigned long) blkvsc_req->sector_start, - blkvsc_req->sector_count, - blkvsc_req->request.DataBuffer.Offset, - blkvsc_req->request.DataBuffer.Length); -#if 0 - for (i = 0; i < (blkvsc_req->request.DataBuffer.Length >> 12); i++) { - DPRINT_DBG(BLKVSC_DRV, "blkvsc_submit_request() - " - "req %p pfn[%d] %llx\n", - blkvsc_req, i, - blkvsc_req->request.DataBuffer.PfnArray[i]); - } -#endif + /* + * Now wait for all outgoing I/O to be drained. + */ + storvsc_wait_to_drain((struct storvsc_device *)dev->ext); - storvsc_req = &blkvsc_req->request; - storvsc_req->Extension = (void *)((unsigned long)blkvsc_req + - sizeof(struct blkvsc_request)); +} - storvsc_req->Type = blkvsc_req->write ? WRITE_TYPE : READ_TYPE; +static int blkvsc_release(struct gendisk *disk, fmode_t mode) +{ + struct block_device_context *blkdev = disk->private_data; + unsigned long flags; - storvsc_req->OnIOCompletion = request_completion; - storvsc_req->Context = blkvsc_req; + spin_lock_irqsave(&blkdev->lock, flags); - storvsc_req->Host = blkdev->port; - storvsc_req->Bus = blkdev->path; - storvsc_req->TargetId = blkdev->target; - storvsc_req->LunId = 0; /* this is not really used at all */ + if ((--blkdev->users == 0) && (blkdev->shutting_down)) { + blk_stop_queue(blkdev->gd->queue); + spin_unlock_irqrestore(&blkdev->lock, flags); - storvsc_req->CdbLen = blkvsc_req->cmd_len; - storvsc_req->Cdb = blkvsc_req->cmnd; + blkvsc_do_operation(blkdev, DO_FLUSH); + del_gendisk(blkdev->gd); + put_disk(blkdev->gd); + blk_cleanup_queue(blkdev->gd->queue); - storvsc_req->SenseBuffer = blkvsc_req->sense_buffer; - storvsc_req->SenseBufferSize = SCSI_SENSE_BUFFERSIZE; + storvsc_dev_remove(blkdev->device_ctx); - ret = storvsc_drv_obj->OnIORequest(&blkdev->device_ctx->device_obj, - &blkvsc_req->request); - if (ret == 0) - blkdev->num_outstanding_reqs++; + kmem_cache_destroy(blkdev->request_pool); + kfree(blkdev); + } else + spin_unlock_irqrestore(&blkdev->lock, flags); - return ret; + return 0; } + /* * We break the request into 1 or more blkvsc_requests and submit * them. If we cant submit them all, we put them on the @@ -926,11 +607,8 @@ static int blkvsc_do_request(struct block_device_context *blkdev, int pending = 0; struct blkvsc_request_group *group = NULL; - DPRINT_DBG(BLKVSC_DRV, "blkdev %p req %p sect %lu\n", blkdev, req, - (unsigned long)blk_rq_pos(req)); - /* Create a group to tie req to list of blkvsc_reqs */ - group = kmem_cache_alloc(blkdev->request_pool, GFP_ATOMIC); + group = kmem_cache_zalloc(blkdev->request_pool, GFP_ATOMIC); if (!group) return -ENOMEM; @@ -946,11 +624,6 @@ static int blkvsc_do_request(struct block_device_context *blkdev, * Map this bio into an existing or new storvsc request */ bio_for_each_segment(bvec, bio, seg_idx) { - DPRINT_DBG(BLKVSC_DRV, "bio_for_each_segment() " - "- req %p bio %p bvec %p seg_idx %d " - "databuf_idx %d\n", req, bio, bvec, - seg_idx, databuf_idx); - /* Get a new storvsc request */ /* 1st-time */ if ((!blkvsc_req) || @@ -962,10 +635,15 @@ static int blkvsc_do_request(struct block_device_context *blkdev, (prev_bvec->bv_len != PAGE_SIZE))) { /* submit the prev one */ if (blkvsc_req) { - blkvsc_req->sector_start = start_sector; - sector_div(blkvsc_req->sector_start, (blkdev->sector_size >> 9)); - - blkvsc_req->sector_count = num_sectors / (blkdev->sector_size >> 9); + blkvsc_req->sector_start = + start_sector; + sector_div( + blkvsc_req->sector_start, + (blkdev->sector_size >> 9)); + + blkvsc_req->sector_count = + num_sectors / + (blkdev->sector_size >> 9); blkvsc_init_rw(blkvsc_req); } @@ -973,18 +651,24 @@ static int blkvsc_do_request(struct block_device_context *blkdev, * Create new blkvsc_req to represent * the current bvec */ - blkvsc_req = kmem_cache_alloc(blkdev->request_pool, GFP_ATOMIC); + blkvsc_req = + kmem_cache_zalloc( + blkdev->request_pool, GFP_ATOMIC); if (!blkvsc_req) { /* free up everything */ list_for_each_entry_safe( blkvsc_req, tmp, &group->blkvsc_req_list, req_entry) { - list_del(&blkvsc_req->req_entry); - kmem_cache_free(blkdev->request_pool, blkvsc_req); + list_del( + &blkvsc_req->req_entry); + kmem_cache_free( + blkdev->request_pool, + blkvsc_req); } - kmem_cache_free(blkdev->request_pool, group); + kmem_cache_free( + blkdev->request_pool, group); return -ENOMEM; } @@ -993,23 +677,32 @@ static int blkvsc_do_request(struct block_device_context *blkdev, blkvsc_req->dev = blkdev; blkvsc_req->req = req; - blkvsc_req->request.DataBuffer.Offset = bvec->bv_offset; - blkvsc_req->request.DataBuffer.Length = 0; + blkvsc_req->request. + data_buffer.offset + = bvec->bv_offset; + blkvsc_req->request. + data_buffer.len = 0; /* Add to the group */ blkvsc_req->group = group; blkvsc_req->group->outstanding++; list_add_tail(&blkvsc_req->req_entry, - &blkvsc_req->group->blkvsc_req_list); + &blkvsc_req->group->blkvsc_req_list); start_sector += num_sectors; num_sectors = 0; databuf_idx = 0; } - /* Add the curr bvec/segment to the curr blkvsc_req */ - blkvsc_req->request.DataBuffer.PfnArray[databuf_idx] = page_to_pfn(bvec->bv_page); - blkvsc_req->request.DataBuffer.Length += bvec->bv_len; + /* + * Add the curr bvec/segment to the curr + * blkvsc_req + */ + blkvsc_req->request.data_buffer. + pfn_array[databuf_idx] + = page_to_pfn(bvec->bv_page); + blkvsc_req->request.data_buffer.len + += bvec->bv_len; prev_bvec = bvec; @@ -1023,10 +716,6 @@ static int blkvsc_do_request(struct block_device_context *blkdev, /* Handle the last one */ if (blkvsc_req) { - DPRINT_DBG(BLKVSC_DRV, "blkdev %p req %p group %p count %d\n", - blkdev, req, blkvsc_req->group, - blkvsc_req->group->outstanding); - blkvsc_req->sector_start = start_sector; sector_div(blkvsc_req->sector_start, (blkdev->sector_size >> 9)); @@ -1039,13 +728,6 @@ static int blkvsc_do_request(struct block_device_context *blkdev, list_for_each_entry(blkvsc_req, &group->blkvsc_req_list, req_entry) { if (pending) { - DPRINT_DBG(BLKVSC_DRV, "adding blkvsc_req to " - "pending_list - blkvsc_req %p start_sect %lu" - " sect_count %ld (%lu %ld)\n", blkvsc_req, - (unsigned long)blkvsc_req->sector_start, - blkvsc_req->sector_count, - (unsigned long)start_sector, - (unsigned long)num_sectors); list_add_tail(&blkvsc_req->pend_entry, &blkdev->pending_list); @@ -1058,186 +740,12 @@ static int blkvsc_do_request(struct block_device_context *blkdev, &blkdev->pending_list); } - DPRINT_DBG(BLKVSC_DRV, "submitted blkvsc_req %p " - "start_sect %lu sect_count %ld (%lu %ld) " - "ret %d\n", blkvsc_req, - (unsigned long)blkvsc_req->sector_start, - blkvsc_req->sector_count, - (unsigned long)start_sector, - num_sectors, ret); } } return pending; } -static void blkvsc_cmd_completion(struct hv_storvsc_request *request) -{ - struct blkvsc_request *blkvsc_req = - (struct blkvsc_request *)request->Context; - struct block_device_context *blkdev = - (struct block_device_context *)blkvsc_req->dev; - struct scsi_sense_hdr sense_hdr; - - DPRINT_DBG(BLKVSC_DRV, "blkvsc_cmd_completion() - req %p\n", - blkvsc_req); - - blkdev->num_outstanding_reqs--; - - if (blkvsc_req->request.Status) - if (scsi_normalize_sense(blkvsc_req->sense_buffer, - SCSI_SENSE_BUFFERSIZE, &sense_hdr)) - scsi_print_sense_hdr("blkvsc", &sense_hdr); - - blkvsc_req->cond = 1; - wake_up_interruptible(&blkvsc_req->wevent); -} - -static void blkvsc_request_completion(struct hv_storvsc_request *request) -{ - struct blkvsc_request *blkvsc_req = - (struct blkvsc_request *)request->Context; - struct block_device_context *blkdev = - (struct block_device_context *)blkvsc_req->dev; - unsigned long flags; - struct blkvsc_request *comp_req, *tmp; - - /* ASSERT(blkvsc_req->group); */ - - DPRINT_DBG(BLKVSC_DRV, "blkdev %p blkvsc_req %p group %p type %s " - "sect_start %lu sect_count %ld len %d group outstd %d " - "total outstd %d\n", - blkdev, blkvsc_req, blkvsc_req->group, - (blkvsc_req->write) ? "WRITE" : "READ", - (unsigned long)blkvsc_req->sector_start, - blkvsc_req->sector_count, - blkvsc_req->request.DataBuffer.Length, - blkvsc_req->group->outstanding, - blkdev->num_outstanding_reqs); - - spin_lock_irqsave(&blkdev->lock, flags); - - blkdev->num_outstanding_reqs--; - blkvsc_req->group->outstanding--; - - /* - * Only start processing when all the blkvsc_reqs are - * completed. This guarantees no out-of-order blkvsc_req - * completion when calling end_that_request_first() - */ - if (blkvsc_req->group->outstanding == 0) { - list_for_each_entry_safe(comp_req, tmp, - &blkvsc_req->group->blkvsc_req_list, - req_entry) { - DPRINT_DBG(BLKVSC_DRV, "completing blkvsc_req %p " - "sect_start %lu sect_count %ld\n", - comp_req, - (unsigned long)comp_req->sector_start, - comp_req->sector_count); - - list_del(&comp_req->req_entry); - - if (!__blk_end_request(comp_req->req, - (!comp_req->request.Status ? 0 : -EIO), - comp_req->sector_count * blkdev->sector_size)) { - /* - * All the sectors have been xferred ie the - * request is done - */ - DPRINT_DBG(BLKVSC_DRV, "req %p COMPLETED\n", - comp_req->req); - kmem_cache_free(blkdev->request_pool, - comp_req->group); - } - - kmem_cache_free(blkdev->request_pool, comp_req); - } - - if (!blkdev->shutting_down) { - blkvsc_do_pending_reqs(blkdev); - blk_start_queue(blkdev->gd->queue); - blkvsc_request(blkdev->gd->queue); - } - } - - spin_unlock_irqrestore(&blkdev->lock, flags); -} - -static int blkvsc_cancel_pending_reqs(struct block_device_context *blkdev) -{ - struct blkvsc_request *pend_req, *tmp; - struct blkvsc_request *comp_req, *tmp2; - - int ret = 0; - - DPRINT_DBG(BLKVSC_DRV, "blkvsc_cancel_pending_reqs()"); - - /* Flush the pending list first */ - list_for_each_entry_safe(pend_req, tmp, &blkdev->pending_list, - pend_entry) { - /* - * The pend_req could be part of a partially completed - * request. If so, complete those req first until we - * hit the pend_req - */ - list_for_each_entry_safe(comp_req, tmp2, - &pend_req->group->blkvsc_req_list, - req_entry) { - DPRINT_DBG(BLKVSC_DRV, "completing blkvsc_req %p " - "sect_start %lu sect_count %ld\n", - comp_req, - (unsigned long) comp_req->sector_start, - comp_req->sector_count); - - if (comp_req == pend_req) - break; - - list_del(&comp_req->req_entry); - - if (comp_req->req) { - ret = __blk_end_request(comp_req->req, - (!comp_req->request.Status ? 0 : -EIO), - comp_req->sector_count * - blkdev->sector_size); - - /* FIXME: shouldn't this do more than return? */ - if (ret) - goto out; - } - - kmem_cache_free(blkdev->request_pool, comp_req); - } - - DPRINT_DBG(BLKVSC_DRV, "cancelling pending request - %p\n", - pend_req); - - list_del(&pend_req->pend_entry); - - list_del(&pend_req->req_entry); - - if (comp_req->req) { - if (!__blk_end_request(pend_req->req, -EIO, - pend_req->sector_count * - blkdev->sector_size)) { - /* - * All the sectors have been xferred ie the - * request is done - */ - DPRINT_DBG(BLKVSC_DRV, - "blkvsc_cancel_pending_reqs() - " - "req %p COMPLETED\n", pend_req->req); - kmem_cache_free(blkdev->request_pool, - pend_req->group); - } - } - - kmem_cache_free(blkdev->request_pool, pend_req); - } - -out: - return ret; -} - static int blkvsc_do_pending_reqs(struct block_device_context *blkdev) { struct blkvsc_request *pend_req, *tmp; @@ -1246,8 +754,6 @@ static int blkvsc_do_pending_reqs(struct block_device_context *blkdev) /* Flush the pending list first */ list_for_each_entry_safe(pend_req, tmp, &blkdev->pending_list, pend_entry) { - DPRINT_DBG(BLKVSC_DRV, "working off pending_list - %p\n", - pend_req); ret = blkvsc_submit_request(pend_req, blkvsc_request_completion); @@ -1260,19 +766,17 @@ static int blkvsc_do_pending_reqs(struct block_device_context *blkdev) return ret; } + static void blkvsc_request(struct request_queue *queue) { struct block_device_context *blkdev = NULL; struct request *req; int ret = 0; - DPRINT_DBG(BLKVSC_DRV, "- enter\n"); while ((req = blk_peek_request(queue)) != NULL) { - DPRINT_DBG(BLKVSC_DRV, "- req %p\n", req); blkdev = req->rq_disk->private_data; - if (blkdev->shutting_down || req->cmd_type != REQ_TYPE_FS || - blkdev->media_not_present) { + if (blkdev->shutting_down || req->cmd_type != REQ_TYPE_FS) { __blk_end_request_cur(req, 0); continue; } @@ -1280,8 +784,6 @@ static void blkvsc_request(struct request_queue *queue) ret = blkvsc_do_pending_reqs(blkdev); if (ret != 0) { - DPRINT_DBG(BLKVSC_DRV, - "- stop queue - pending_list not empty\n"); blk_stop_queue(queue); break; } @@ -1290,11 +792,9 @@ static void blkvsc_request(struct request_queue *queue) ret = blkvsc_do_request(blkdev, req); if (ret > 0) { - DPRINT_DBG(BLKVSC_DRV, "- stop queue - no room\n"); blk_stop_queue(queue); break; } else if (ret < 0) { - DPRINT_DBG(BLKVSC_DRV, "- stop queue - no mem\n"); blk_requeue_request(queue, req); blk_stop_queue(queue); break; @@ -1302,186 +802,216 @@ static void blkvsc_request(struct request_queue *queue) } } -static int blkvsc_open(struct block_device *bdev, fmode_t mode) -{ - struct block_device_context *blkdev = bdev->bd_disk->private_data; - - DPRINT_DBG(BLKVSC_DRV, "- users %d disk %s\n", blkdev->users, - blkdev->gd->disk_name); - - spin_lock(&blkdev->lock); - if (!blkdev->users && blkdev->device_type == DVD_TYPE) { - spin_unlock(&blkdev->lock); - check_disk_change(bdev); - spin_lock(&blkdev->lock); - } - blkdev->users++; +/* The one and only one */ +static struct hv_driver blkvsc_drv = { + .probe = blkvsc_probe, + .remove = blkvsc_remove, + .shutdown = blkvsc_shutdown, +}; - spin_unlock(&blkdev->lock); - return 0; -} +static const struct block_device_operations block_ops = { + .owner = THIS_MODULE, + .open = blkvsc_open, + .release = blkvsc_release, + .getgeo = blkvsc_getgeo, + .ioctl = blkvsc_ioctl, +}; -static int blkvsc_release(struct gendisk *disk, fmode_t mode) +/* + * blkvsc_drv_init - BlkVsc driver initialization. + */ +static int blkvsc_drv_init(void) { - struct block_device_context *blkdev = disk->private_data; + struct hv_driver *drv = &blkvsc_drv; + int ret; - DPRINT_DBG(BLKVSC_DRV, "- users %d disk %s\n", blkdev->users, - blkdev->gd->disk_name); + BUILD_BUG_ON(sizeof(sector_t) != 8); - spin_lock(&blkdev->lock); - if (blkdev->users == 1) { - spin_unlock(&blkdev->lock); - blkvsc_do_flush(blkdev); - spin_lock(&blkdev->lock); - } + memcpy(&drv->dev_type, &dev_type, sizeof(struct hv_guid)); + drv->driver.name = drv_name; - blkdev->users--; + /* The driver belongs to vmbus */ + ret = vmbus_child_driver_register(&drv->driver); - spin_unlock(&blkdev->lock); - return 0; + return ret; } -static int blkvsc_media_changed(struct gendisk *gd) + +static void blkvsc_drv_exit(void) { - DPRINT_DBG(BLKVSC_DRV, "- enter\n"); - return 1; + + vmbus_child_driver_unregister(&blkvsc_drv.driver); } -static int blkvsc_revalidate_disk(struct gendisk *gd) +/* + * blkvsc_probe - Add a new device for this driver + */ +static int blkvsc_probe(struct hv_device *dev) { - struct block_device_context *blkdev = gd->private_data; - - DPRINT_DBG(BLKVSC_DRV, "- enter\n"); + struct block_device_context *blkdev = NULL; + struct storvsc_device_info device_info; + struct storvsc_major_info major_info; + int ret = 0; - if (blkdev->device_type == DVD_TYPE) { - blkvsc_do_read_capacity(blkdev); - set_capacity(blkdev->gd, blkdev->capacity * - (blkdev->sector_size/512)); - blk_queue_logical_block_size(gd->queue, blkdev->sector_size); + blkdev = kzalloc(sizeof(struct block_device_context), GFP_KERNEL); + if (!blkdev) { + ret = -ENOMEM; + goto cleanup; } - return 0; -} -static int blkvsc_getgeo(struct block_device *bd, struct hd_geometry *hg) -{ - sector_t total_sectors = get_capacity(bd->bd_disk); - sector_t cylinder_times_heads = 0; - sector_t temp = 0; + INIT_LIST_HEAD(&blkdev->pending_list); - int sectors_per_track = 0; - int heads = 0; - int cylinders = 0; - int rem = 0; + /* Initialize what we can here */ + spin_lock_init(&blkdev->lock); - if (total_sectors > (65535 * 16 * 255)) - total_sectors = (65535 * 16 * 255); - if (total_sectors >= (65535 * 16 * 63)) { - sectors_per_track = 255; - heads = 16; + blkdev->request_pool = kmem_cache_create(dev_name(&dev->device), + sizeof(struct blkvsc_request), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!blkdev->request_pool) { + ret = -ENOMEM; + goto cleanup; + } - cylinder_times_heads = total_sectors; - /* sector_div stores the quotient in cylinder_times_heads */ - rem = sector_div(cylinder_times_heads, sectors_per_track); - } else { - sectors_per_track = 17; - cylinder_times_heads = total_sectors; - /* sector_div stores the quotient in cylinder_times_heads */ - rem = sector_div(cylinder_times_heads, sectors_per_track); + ret = blkvsc_device_add(dev, &device_info); + if (ret != 0) + goto cleanup; - temp = cylinder_times_heads + 1023; - /* sector_div stores the quotient in temp */ - rem = sector_div(temp, 1024); + blkdev->device_ctx = dev; + /* this identified the device 0 or 1 */ + blkdev->target = device_info.target_id; + /* this identified the ide ctrl 0 or 1 */ + blkdev->path = device_info.path_id; - heads = temp; + dev_set_drvdata(&dev->device, blkdev); - if (heads < 4) - heads = 4; + ret = storvsc_get_major_info(&device_info, &major_info); + if (ret) + goto cleanup; - if (cylinder_times_heads >= (heads * 1024) || (heads > 16)) { - sectors_per_track = 31; - heads = 16; + if (major_info.do_register) { + ret = register_blkdev(major_info.major, major_info.devname); - cylinder_times_heads = total_sectors; - /* - * sector_div stores the quotient in - * cylinder_times_heads - */ - rem = sector_div(cylinder_times_heads, - sectors_per_track); + if (ret != 0) { + DPRINT_ERR(BLKVSC_DRV, + "register_blkdev() failed! ret %d", ret); + goto remove; } + } - if (cylinder_times_heads >= (heads * 1024)) { - sectors_per_track = 63; - heads = 16; + DPRINT_INFO(BLKVSC_DRV, "blkvsc registered for major %d!!", + major_info.major); - cylinder_times_heads = total_sectors; - /* - * sector_div stores the quotient in - * cylinder_times_heads - */ - rem = sector_div(cylinder_times_heads, - sectors_per_track); - } + blkdev->gd = alloc_disk(BLKVSC_MINORS); + if (!blkdev->gd) { + ret = -1; + goto cleanup; } - temp = cylinder_times_heads; - /* sector_div stores the quotient in temp */ - rem = sector_div(temp, heads); - cylinders = temp; + blkdev->gd->queue = blk_init_queue(blkvsc_request, &blkdev->lock); - hg->heads = heads; - hg->sectors = sectors_per_track; - hg->cylinders = cylinders; + blk_queue_max_segment_size(blkdev->gd->queue, PAGE_SIZE); + blk_queue_max_segments(blkdev->gd->queue, MAX_MULTIPAGE_BUFFER_COUNT); + blk_queue_segment_boundary(blkdev->gd->queue, PAGE_SIZE-1); + blk_queue_bounce_limit(blkdev->gd->queue, BLK_BOUNCE_ANY); + blk_queue_dma_alignment(blkdev->gd->queue, 511); - DPRINT_INFO(BLKVSC_DRV, "CHS (%d, %d, %d)", cylinders, heads, - sectors_per_track); + blkdev->gd->major = major_info.major; + if (major_info.index == 1 || major_info.index == 3) + blkdev->gd->first_minor = BLKVSC_MINORS; + else + blkdev->gd->first_minor = 0; + blkdev->gd->fops = &block_ops; + blkdev->gd->private_data = blkdev; + blkdev->gd->driverfs_dev = &(blkdev->device_ctx->device); + sprintf(blkdev->gd->disk_name, "hd%c", 'a' + major_info.index); - return 0; -} + blkvsc_do_operation(blkdev, DO_INQUIRY); + blkvsc_do_operation(blkdev, DO_CAPACITY); -static int blkvsc_ioctl(struct block_device *bd, fmode_t mode, - unsigned cmd, unsigned long argument) -{ -/* struct block_device_context *blkdev = bd->bd_disk->private_data; */ - int ret; + set_capacity(blkdev->gd, blkdev->capacity * (blkdev->sector_size/512)); + blk_queue_logical_block_size(blkdev->gd->queue, blkdev->sector_size); + /* go! */ + add_disk(blkdev->gd); - switch (cmd) { - /* - * TODO: I think there is certain format for HDIO_GET_IDENTITY rather - * than just a GUID. Commented it out for now. - */ -#if 0 - case HDIO_GET_IDENTITY: - DPRINT_INFO(BLKVSC_DRV, "HDIO_GET_IDENTITY\n"); - if (copy_to_user((void __user *)arg, blkdev->device_id, - blkdev->device_id_len)) - ret = -EFAULT; - break; -#endif - default: - ret = -EINVAL; - break; + DPRINT_INFO(BLKVSC_DRV, "%s added!! capacity %lu sector_size %d", + blkdev->gd->disk_name, (unsigned long)blkdev->capacity, + blkdev->sector_size); + + return ret; + +remove: + storvsc_dev_remove(dev); + +cleanup: + if (blkdev) { + if (blkdev->request_pool) { + kmem_cache_destroy(blkdev->request_pool); + blkdev->request_pool = NULL; + } + kfree(blkdev); + blkdev = NULL; } return ret; } -static int __init blkvsc_init(void) +static void blkvsc_request_completion(struct hv_storvsc_request *request) { - int ret; + struct blkvsc_request *blkvsc_req = + (struct blkvsc_request *)request->context; + struct block_device_context *blkdev = + (struct block_device_context *)blkvsc_req->dev; + unsigned long flags; + struct blkvsc_request *comp_req, *tmp; + struct vmscsi_request *vm_srb; - BUILD_BUG_ON(sizeof(sector_t) != 8); - DPRINT_INFO(BLKVSC_DRV, "Blkvsc initializing...."); + spin_lock_irqsave(&blkdev->lock, flags); - ret = blkvsc_drv_init(BlkVscInitialize); + blkdev->num_outstanding_reqs--; + blkvsc_req->group->outstanding--; - return ret; + /* + * Only start processing when all the blkvsc_reqs are + * completed. This guarantees no out-of-order blkvsc_req + * completion when calling end_that_request_first() + */ + if (blkvsc_req->group->outstanding == 0) { + list_for_each_entry_safe(comp_req, tmp, + &blkvsc_req->group->blkvsc_req_list, + req_entry) { + + list_del(&comp_req->req_entry); + + vm_srb = + &comp_req->request.vstor_packet.vm_srb; + if (!__blk_end_request(comp_req->req, + (!vm_srb->scsi_status ? 0 : -EIO), + comp_req->sector_count * blkdev->sector_size)) { + /* + * All the sectors have been xferred ie the + * request is done + */ + kmem_cache_free(blkdev->request_pool, + comp_req->group); + } + + kmem_cache_free(blkdev->request_pool, comp_req); + } + + if (!blkdev->shutting_down) { + blkvsc_do_pending_reqs(blkdev); + blk_start_queue(blkdev->gd->queue); + blkvsc_request(blkdev->gd->queue); + } + } + + spin_unlock_irqrestore(&blkdev->lock, flags); } static void __exit blkvsc_exit(void) @@ -1492,5 +1022,5 @@ static void __exit blkvsc_exit(void) MODULE_LICENSE("GPL"); MODULE_VERSION(HV_DRV_VERSION); MODULE_DESCRIPTION("Microsoft Hyper-V virtual block driver"); -module_init(blkvsc_init); +module_init(blkvsc_drv_init); module_exit(blkvsc_exit); diff --git a/drivers/staging/hv/channel.c b/drivers/staging/hv/channel.c new file mode 100644 index 0000000..cffca7c --- a/dev/null +++ b/drivers/staging/hv/channel.c @@ -0,0 +1,877 @@ +/* + * 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> + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/module.h> + +#include "hyperv.h" +#include "hyperv_vmbus.h" + +#define NUM_PAGES_SPANNED(addr, len) \ +((PAGE_ALIGN(addr + len) >> PAGE_SHIFT) - (addr >> PAGE_SHIFT)) + +/* Internal routines */ +static int create_gpadl_header( + void *kbuffer, /* must be phys and virt contiguous */ + u32 size, /* page-size multiple */ + struct vmbus_channel_msginfo **msginfo, + u32 *messagecount); +static void vmbus_setevent(struct vmbus_channel *channel); + +/* + * vmbus_setevent- Trigger an event notification on the specified + * channel. + */ +static void vmbus_setevent(struct vmbus_channel *channel) +{ + struct hv_monitor_page *monitorpage; + + if (channel->offermsg.monitor_allocated) { + /* Each u32 represents 32 channels */ + sync_set_bit(channel->offermsg.child_relid & 31, + (unsigned long *) vmbus_connection.send_int_page + + (channel->offermsg.child_relid >> 5)); + + monitorpage = vmbus_connection.monitor_pages; + monitorpage++; /* Get the child to parent monitor page */ + + sync_set_bit(channel->monitor_bit, + (unsigned long *)&monitorpage->trigger_group + [channel->monitor_grp].pending); + + } else { + vmbus_set_event(channel->offermsg.child_relid); + } +} + +/* + * vmbus_get_debug_info -Retrieve various channel debug info + */ +void vmbus_get_debug_info(struct vmbus_channel *channel, + struct vmbus_channel_debug_info *debuginfo) +{ + struct hv_monitor_page *monitorpage; + u8 monitor_group = (u8)channel->offermsg.monitorid / 32; + u8 monitor_offset = (u8)channel->offermsg.monitorid % 32; + /* u32 monitorBit = 1 << monitorOffset; */ + + debuginfo->relid = channel->offermsg.child_relid; + debuginfo->state = channel->state; + memcpy(&debuginfo->interfacetype, + &channel->offermsg.offer.if_type, sizeof(struct hv_guid)); + memcpy(&debuginfo->interface_instance, + &channel->offermsg.offer.if_instance, + sizeof(struct hv_guid)); + + monitorpage = (struct hv_monitor_page *)vmbus_connection.monitor_pages; + + debuginfo->monitorid = channel->offermsg.monitorid; + + debuginfo->servermonitor_pending = + monitorpage->trigger_group[monitor_group].pending; + debuginfo->servermonitor_latency = + monitorpage->latency[monitor_group][monitor_offset]; + debuginfo->servermonitor_connectionid = + monitorpage->parameter[monitor_group] + [monitor_offset].connectionid.u.id; + + monitorpage++; + + debuginfo->clientmonitor_pending = + monitorpage->trigger_group[monitor_group].pending; + debuginfo->clientmonitor_latency = + monitorpage->latency[monitor_group][monitor_offset]; + debuginfo->clientmonitor_connectionid = + monitorpage->parameter[monitor_group] + [monitor_offset].connectionid.u.id; + + hv_ringbuffer_get_debuginfo(&channel->inbound, &debuginfo->inbound); + hv_ringbuffer_get_debuginfo(&channel->outbound, &debuginfo->outbound); +} + +/* + * vmbus_open - Open the specified channel. + */ +int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, + u32 recv_ringbuffer_size, 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, t, err = 0; + + newchannel->onchannel_callback = onchannelcallback; + newchannel->channel_callback_context = context; + + /* Allocate the ring buffer */ + out = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, + get_order(send_ringbuffer_size + recv_ringbuffer_size)); + + if (!out) + return -ENOMEM; + + + in = (void *)((unsigned long)out + send_ringbuffer_size); + + newchannel->ringbuffer_pages = out; + newchannel->ringbuffer_pagecount = (send_ringbuffer_size + + recv_ringbuffer_size) >> PAGE_SHIFT; + + ret = hv_ringbuffer_init( + &newchannel->outbound, out, send_ringbuffer_size); + + if (ret != 0) { + err = ret; + goto errorout; + } + + ret = hv_ringbuffer_init( + &newchannel->inbound, in, recv_ringbuffer_size); + if (ret != 0) { + err = ret; + goto errorout; + } + + + /* Establish the gpadl for the ring buffer */ + newchannel->ringbuffer_gpadlhandle = 0; + + ret = vmbus_establish_gpadl(newchannel, + newchannel->outbound.ring_buffer, + send_ringbuffer_size + + recv_ringbuffer_size, + &newchannel->ringbuffer_gpadlhandle); + + if (ret != 0) { + err = ret; + goto errorout; + } + + /* 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; + } + + init_completion(&openInfo->waitevent); + + openMsg = (struct vmbus_channel_open_channel *)openInfo->msg; + openMsg->header.msgtype = CHANNELMSG_OPENCHANNEL; + openMsg->openid = newchannel->offermsg.child_relid; + openMsg->child_relid = newchannel->offermsg.child_relid; + openMsg->ringbuffer_gpadlhandle = newchannel->ringbuffer_gpadlhandle; + openMsg->downstream_ringbuffer_pageoffset = send_ringbuffer_size >> + PAGE_SHIFT; + openMsg->server_contextarea_gpadlhandle = 0; + + if (userdatalen > MAX_USER_DEFINED_BYTES) { + err = -EINVAL; + goto errorout; + } + + if (userdatalen) + memcpy(openMsg->userdata, userdata, userdatalen); + + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_add_tail(&openInfo->msglistentry, + &vmbus_connection.chn_msg_list); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + + ret = vmbus_post_msg(openMsg, + sizeof(struct vmbus_channel_open_channel)); + + if (ret != 0) + goto cleanup; + + t = wait_for_completion_timeout(&openInfo->waitevent, HZ); + if (t == 0) { + err = -ETIMEDOUT; + goto errorout; + } + + + if (openInfo->response.open_result.status) + err = openInfo->response.open_result.status; + +cleanup: + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_del(&openInfo->msglistentry); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + + kfree(openInfo); + return err; + +errorout: + hv_ringbuffer_cleanup(&newchannel->outbound); + hv_ringbuffer_cleanup(&newchannel->inbound); + free_pages((unsigned long)out, + get_order(send_ringbuffer_size + recv_ringbuffer_size)); + kfree(openInfo); + return err; +} +EXPORT_SYMBOL_GPL(vmbus_open); + +/* + * dump_gpadl_body - Dump the gpadl body message to the console for + * debugging purposes. + */ +static void dump_gpadl_body(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]); +} + +/* + * dump_gpadl_header - Dump the gpadl header message to the console for + * debugging purposes. + */ +static void dump_gpadl_header(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->child_relid, gpadl->rangecount, gpadl->range_buflen); + for (i = 0; i < gpadl->rangecount; i++) { + pagecount = gpadl->range[i].byte_count >> PAGE_SHIFT; + pagecount = (pagecount > 26) ? 26 : pagecount; + + DPRINT_DBG(VMBUS, "gpadl range %d - len %d offset %d " + "page count %d", i, gpadl->range[i].byte_count, + gpadl->range[i].byte_offset, pagecount); + + for (j = 0; j < pagecount; j++) + DPRINT_DBG(VMBUS, "%d) pfn %llu", j, + gpadl->range[i].pfn_array[j]); + } +} + +/* + * create_gpadl_header - Creates a gpadl for the specified buffer + */ +static int create_gpadl_header(void *kbuffer, u32 size, + struct vmbus_channel_msginfo **msginfo, + u32 *messagecount) +{ + int i; + int pagecount; + unsigned long long pfn; + struct vmbus_channel_gpadl_header *gpadl_header; + struct vmbus_channel_gpadl_body *gpadl_body; + struct vmbus_channel_msginfo *msgheader; + struct vmbus_channel_msginfo *msgbody = NULL; + u32 msgsize; + + int pfnsum, pfncount, pfnleft, pfncurr, pfnsize; + + 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->msgsize = msgsize; + + gpadl_header = (struct vmbus_channel_gpadl_header *) + msgheader->msg; + gpadl_header->rangecount = 1; + gpadl_header->range_buflen = sizeof(struct gpa_range) + + pagecount * sizeof(u64); + gpadl_header->range[0].byte_offset = 0; + gpadl_header->range[0].byte_count = size; + for (i = 0; i < pfncount; i++) + gpadl_header->range[0].pfn_array[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); + + if (!msgbody) { + struct vmbus_channel_msginfo *pos = NULL; + struct vmbus_channel_msginfo *tmp = NULL; + /* + * Free up all the allocated messages. + */ + list_for_each_entry_safe(pos, tmp, + &msgheader->submsglist, + msglistentry) { + + list_del(&pos->msglistentry); + kfree(pos); + } + + goto nomem; + } + + msgbody->msgsize = msgsize; + (*messagecount)++; + gpadl_body = + (struct vmbus_channel_gpadl_body *)msgbody->msg; + + /* + * Gpadl is u32 and we are using a pointer which could + * be 64-bit + * This is governed by the guest/host protocol and + * so the hypervisor gurantees that this is ok. + */ + for (i = 0; i < pfncurr; i++) + gpadl_body->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); + if (msgheader == NULL) + goto nomem; + msgheader->msgsize = msgsize; + + gpadl_header = (struct vmbus_channel_gpadl_header *) + msgheader->msg; + gpadl_header->rangecount = 1; + gpadl_header->range_buflen = sizeof(struct gpa_range) + + pagecount * sizeof(u64); + gpadl_header->range[0].byte_offset = 0; + gpadl_header->range[0].byte_count = size; + for (i = 0; i < pagecount; i++) + gpadl_header->range[0].pfn_array[i] = pfn+i; + + *msginfo = msgheader; + *messagecount = 1; + } + + return 0; +nomem: + kfree(msgheader); + kfree(msgbody); + return -ENOMEM; +} + +/* + * vmbus_establish_gpadl - Estabish a GPADL for the specified buffer + * + * @channel: a channel + * @kbuffer: from kmalloc + * @size: page-size multiple + * @gpadl_handle: some funky thing + */ +int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, + u32 size, u32 *gpadl_handle) +{ + struct vmbus_channel_gpadl_header *gpadlmsg; + struct vmbus_channel_gpadl_body *gpadl_body; + /* struct vmbus_channel_gpadl_created *gpadlCreated; */ + struct vmbus_channel_msginfo *msginfo = NULL; + struct vmbus_channel_msginfo *submsginfo; + u32 msgcount; + struct list_head *curr; + u32 next_gpadl_handle; + unsigned long flags; + int ret = 0; + int t; + + next_gpadl_handle = atomic_read(&vmbus_connection.next_gpadl_handle); + atomic_inc(&vmbus_connection.next_gpadl_handle); + + ret = create_gpadl_header(kbuffer, size, &msginfo, &msgcount); + if (ret) + return ret; + + init_completion(&msginfo->waitevent); + + gpadlmsg = (struct vmbus_channel_gpadl_header *)msginfo->msg; + gpadlmsg->header.msgtype = CHANNELMSG_GPADL_HEADER; + gpadlmsg->child_relid = channel->offermsg.child_relid; + gpadlmsg->gpadl = next_gpadl_handle; + + dump_gpadl_header(gpadlmsg); + + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_add_tail(&msginfo->msglistentry, + &vmbus_connection.chn_msg_list); + + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + + ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize - + sizeof(*msginfo)); + if (ret != 0) + goto cleanup; + + if (msgcount > 1) { + list_for_each(curr, &msginfo->submsglist) { + + submsginfo = (struct vmbus_channel_msginfo *)curr; + gpadl_body = + (struct vmbus_channel_gpadl_body *)submsginfo->msg; + + gpadl_body->header.msgtype = + CHANNELMSG_GPADL_BODY; + gpadl_body->gpadl = next_gpadl_handle; + + dump_gpadl_body(gpadl_body, submsginfo->msgsize - + sizeof(*submsginfo)); + ret = vmbus_post_msg(gpadl_body, + submsginfo->msgsize - + sizeof(*submsginfo)); + if (ret != 0) + goto cleanup; + + } + } + t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); + BUG_ON(t == 0); + + + /* At this point, we received the gpadl created msg */ + *gpadl_handle = gpadlmsg->gpadl; + +cleanup: + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_del(&msginfo->msglistentry); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + + kfree(msginfo); + return ret; +} +EXPORT_SYMBOL_GPL(vmbus_establish_gpadl); + +/* + * vmbus_teardown_gpadl -Teardown the specified GPADL handle + */ +int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) +{ + struct vmbus_channel_gpadl_teardown *msg; + struct vmbus_channel_msginfo *info; + unsigned long flags; + int ret, t; + + /* ASSERT(gpadl_handle != 0); */ + + info = kmalloc(sizeof(*info) + + sizeof(struct vmbus_channel_gpadl_teardown), GFP_KERNEL); + if (!info) + return -ENOMEM; + + init_completion(&info->waitevent); + + msg = (struct vmbus_channel_gpadl_teardown *)info->msg; + + msg->header.msgtype = CHANNELMSG_GPADL_TEARDOWN; + msg->child_relid = channel->offermsg.child_relid; + msg->gpadl = gpadl_handle; + + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_add_tail(&info->msglistentry, + &vmbus_connection.chn_msg_list); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + ret = vmbus_post_msg(msg, + sizeof(struct vmbus_channel_gpadl_teardown)); + + BUG_ON(ret != 0); + t = wait_for_completion_timeout(&info->waitevent, 5*HZ); + BUG_ON(t == 0); + + /* Received a torndown response */ + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_del(&info->msglistentry); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + + kfree(info); + return ret; +} +EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl); + +/* + * vmbus_close - Close the specified channel + */ +void vmbus_close(struct vmbus_channel *channel) +{ + struct vmbus_channel_close_channel *msg; + int ret; + + /* Stop callback and cancel the timer asap */ + channel->onchannel_callback = NULL; + + /* Send a closing message */ + + msg = &channel->close_msg.msg; + + msg->header.msgtype = CHANNELMSG_CLOSECHANNEL; + msg->child_relid = channel->offermsg.child_relid; + + ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_close_channel)); + + BUG_ON(ret != 0); + /* Tear down the gpadl for the channel's ring buffer */ + if (channel->ringbuffer_gpadlhandle) + vmbus_teardown_gpadl(channel, + channel->ringbuffer_gpadlhandle); + + /* Cleanup the ring buffers for this channel */ + hv_ringbuffer_cleanup(&channel->outbound); + hv_ringbuffer_cleanup(&channel->inbound); + + free_pages((unsigned long)channel->ringbuffer_pages, + get_order(channel->ringbuffer_pagecount * PAGE_SIZE)); + + +} +EXPORT_SYMBOL_GPL(vmbus_close); + +/** + * vmbus_sendpacket() - 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 + * @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 vmbus_sendpacket(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 packetlen_aligned = ALIGN(packetlen, sizeof(u64)); + struct scatterlist bufferlist[3]; + u64 aligned_data = 0; + int ret; + + + /* Setup the descriptor */ + desc.type = type; /* VmbusPacketTypeDataInBand; */ + desc.flags = flags; /* VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; */ + /* in 8-bytes granularity */ + desc.offset8 = sizeof(struct vmpacket_descriptor) >> 3; + desc.len8 = (u16)(packetlen_aligned >> 3); + desc.trans_id = 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], &aligned_data, + packetlen_aligned - packetlen); + + ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); + + if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) + vmbus_setevent(channel); + + return ret; +} +EXPORT_SYMBOL(vmbus_sendpacket); + +/* + * vmbus_sendpacket_pagebuffer - Send a range of single-page buffer + * packets using a GPADL Direct packet type. + */ +int vmbus_sendpacket_pagebuffer(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 packetlen_aligned; + struct scatterlist bufferlist[3]; + u64 aligned_data = 0; + + if (pagecount > MAX_PAGE_BUFFER_COUNT) + return -EINVAL; + + + /* + * 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; + packetlen_aligned = ALIGN(packetlen, sizeof(u64)); + + /* Setup the descriptor */ + desc.type = VM_PKT_DATA_USING_GPA_DIRECT; + desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; + desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */ + desc.length8 = (u16)(packetlen_aligned >> 3); + desc.transactionid = requestid; + desc.rangecount = pagecount; + + for (i = 0; i < pagecount; i++) { + desc.range[i].len = pagebuffers[i].len; + 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], &aligned_data, + packetlen_aligned - packetlen); + + ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); + + if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) + vmbus_setevent(channel); + + return ret; +} +EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer); + +/* + * vmbus_sendpacket_multipagebuffer - Send a multi-page buffer packet + * using a GPADL Direct packet type. + */ +int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, + struct hv_multipage_buffer *multi_pagebuffer, + void *buffer, u32 bufferlen, u64 requestid) +{ + int ret; + struct vmbus_channel_packet_multipage_buffer desc; + u32 descsize; + u32 packetlen; + u32 packetlen_aligned; + struct scatterlist bufferlist[3]; + u64 aligned_data = 0; + u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset, + multi_pagebuffer->len); + + + if ((pfncount < 0) || (pfncount > MAX_MULTIPAGE_BUFFER_COUNT)) + return -EINVAL; + + /* + * Adjust the size down since vmbus_channel_packet_multipage_buffer is + * the largest size we support + */ + descsize = sizeof(struct vmbus_channel_packet_multipage_buffer) - + ((MAX_MULTIPAGE_BUFFER_COUNT - pfncount) * + sizeof(u64)); + packetlen = descsize + bufferlen; + packetlen_aligned = ALIGN(packetlen, sizeof(u64)); + + + /* Setup the descriptor */ + desc.type = VM_PKT_DATA_USING_GPA_DIRECT; + desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; + desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */ + desc.length8 = (u16)(packetlen_aligned >> 3); + desc.transactionid = requestid; + desc.rangecount = 1; + + desc.range.len = multi_pagebuffer->len; + desc.range.offset = multi_pagebuffer->offset; + + memcpy(desc.range.pfn_array, multi_pagebuffer->pfn_array, + 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], &aligned_data, + packetlen_aligned - packetlen); + + ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); + + if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) + vmbus_setevent(channel); + + return ret; +} +EXPORT_SYMBOL_GPL(vmbus_sendpacket_multipagebuffer); + +/** + * vmbus_recvpacket() - 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 + * @buffer_actual_len: 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 vmbus_recvpacket(struct vmbus_channel *channel, void *buffer, + u32 bufferlen, u32 *buffer_actual_len, u64 *requestid) +{ + struct vmpacket_descriptor desc; + u32 packetlen; + u32 userlen; + int ret; + unsigned long flags; + + *buffer_actual_len = 0; + *requestid = 0; + + spin_lock_irqsave(&channel->inbound_lock, flags); + + ret = hv_ringbuffer_peek(&channel->inbound, &desc, + sizeof(struct vmpacket_descriptor)); + if (ret != 0) { + spin_unlock_irqrestore(&channel->inbound_lock, flags); + return 0; + } + + packetlen = desc.len8 << 3; + userlen = packetlen - (desc.offset8 << 3); + + *buffer_actual_len = userlen; + + if (userlen > bufferlen) { + spin_unlock_irqrestore(&channel->inbound_lock, flags); + + pr_err("Buffer too small - got %d needs %d\n", + bufferlen, userlen); + return -ETOOSMALL; + } + + *requestid = desc.trans_id; + + /* Copy over the packet to the user buffer */ + ret = hv_ringbuffer_read(&channel->inbound, buffer, userlen, + (desc.offset8 << 3)); + + spin_unlock_irqrestore(&channel->inbound_lock, flags); + + return 0; +} +EXPORT_SYMBOL(vmbus_recvpacket); + +/* + * vmbus_recvpacket_raw - Retrieve the raw packet on the specified channel + */ +int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer, + u32 bufferlen, u32 *buffer_actual_len, + u64 *requestid) +{ + struct vmpacket_descriptor desc; + u32 packetlen; + u32 userlen; + int ret; + unsigned long flags; + + *buffer_actual_len = 0; + *requestid = 0; + + spin_lock_irqsave(&channel->inbound_lock, flags); + + ret = hv_ringbuffer_peek(&channel->inbound, &desc, + sizeof(struct vmpacket_descriptor)); + if (ret != 0) { + spin_unlock_irqrestore(&channel->inbound_lock, flags); + return 0; + } + + + packetlen = desc.len8 << 3; + userlen = packetlen - (desc.offset8 << 3); + + *buffer_actual_len = packetlen; + + if (packetlen > bufferlen) { + spin_unlock_irqrestore(&channel->inbound_lock, flags); + + pr_err("Buffer too small - needed %d bytes but " + "got space for only %d bytes\n", + packetlen, bufferlen); + return -2; + } + + *requestid = desc.trans_id; + + /* Copy over the entire packet to the user buffer */ + ret = hv_ringbuffer_read(&channel->inbound, buffer, packetlen, 0); + + spin_unlock_irqrestore(&channel->inbound_lock, flags); + return 0; +} +EXPORT_SYMBOL_GPL(vmbus_recvpacket_raw); diff --git a/drivers/staging/hv/channel_mgmt.c b/drivers/staging/hv/channel_mgmt.c new file mode 100644 index 0000000..bf011f3 --- a/dev/null +++ b/drivers/staging/hv/channel_mgmt.c @@ -0,0 +1,784 @@ +/* + * 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> + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/completion.h> + +#include "hyperv.h" +#include "hyperv_vmbus.h" + +struct vmbus_channel_message_table_entry { + enum vmbus_channel_message_type message_type; + void (*message_handler)(struct vmbus_channel_message_header *msg); +}; + +#define MAX_MSG_TYPES 4 +#define MAX_NUM_DEVICE_CLASSES_SUPPORTED 8 + +static const struct hv_guid + supported_device_classes[MAX_NUM_DEVICE_CLASSES_SUPPORTED] = { + /* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */ + /* Storage - SCSI */ + { + .data = { + 0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, + 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f + } + }, + + /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */ + /* Network */ + { + .data = { + 0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, + 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E + } + }, + + /* {CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A} */ + /* Input */ + { + .data = { + 0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, + 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A + } + }, + + /* {32412632-86cb-44a2-9b5c-50d1417354f5} */ + /* IDE */ + { + .data = { + 0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, + 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5 + } + }, + /* 0E0B6031-5213-4934-818B-38D90CED39DB */ + /* Shutdown */ + { + .data = { + 0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, + 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB + } + }, + /* {9527E630-D0AE-497b-ADCE-E80AB0175CAF} */ + /* TimeSync */ + { + .data = { + 0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, + 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf + } + }, + /* {57164f39-9115-4e78-ab55-382f3bd5422d} */ + /* Heartbeat */ + { + .data = { + 0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, + 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d + } + }, + /* {A9A0F4E7-5A45-4d96-B827-8A841E8C03E6} */ + /* KVP */ + { + .data = { + 0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, + 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6 + } + }, + +}; + + +/** + * prep_negotiate_resp() - Create default response for Hyper-V Negotiate message + * @icmsghdrp: Pointer to msg header structure + * @icmsg_negotiate: Pointer to negotiate message structure + * @buf: Raw buffer channel data + * + * @icmsghdrp is of type &struct icmsg_hdr. + * @negop is of type &struct icmsg_negotiate. + * Set up and fill in default negotiate response message. This response can + * come from both the vmbus driver and the hv_utils driver. The current api + * will respond properly to both Windows 2008 and Windows 2008-R2 operating + * systems. + * + * Mainly used by Hyper-V drivers. + */ +void prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, + struct icmsg_negotiate *negop, + u8 *buf) +{ + if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { + icmsghdrp->icmsgsize = 0x10; + + negop = (struct icmsg_negotiate *)&buf[ + sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + + if (negop->icframe_vercnt == 2 && + negop->icversion_data[1].major == 3) { + negop->icversion_data[0].major = 3; + negop->icversion_data[0].minor = 0; + negop->icversion_data[1].major = 3; + negop->icversion_data[1].minor = 0; + } else { + negop->icversion_data[0].major = 1; + negop->icversion_data[0].minor = 0; + negop->icversion_data[1].major = 1; + negop->icversion_data[1].minor = 0; + } + + negop->icframe_vercnt = 1; + negop->icmsg_vercnt = 1; + } +} +EXPORT_SYMBOL(prep_negotiate_resp); + +/** + * chn_cb_negotiate() - Default handler for non IDE/SCSI/NETWORK + * Hyper-V requests + * @context: Pointer to argument structure. + * + * Set up the default handler for non device driver specific requests + * from Hyper-V. This stub responds to the default negotiate messages + * that come in for every non IDE/SCSI/Network request. + * This behavior is normally overwritten in the hv_utils driver. That + * driver handles requests like graceful shutdown, heartbeats etc. + * + * Mainly used by Hyper-V drivers. + */ +void chn_cb_negotiate(void *context) +{ + struct vmbus_channel *channel = context; + u8 *buf; + u32 buflen, recvlen; + u64 requestid; + + struct icmsg_hdr *icmsghdrp; + struct icmsg_negotiate *negop = NULL; + + if (channel->util_index >= 0) { + /* + * This is a properly initialized util channel. + * Route this callback appropriately and setup state + * so that we don't need to reroute again. + */ + if (hv_cb_utils[channel->util_index].callback != NULL) { + /* + * The util driver has established a handler for + * this service; do the magic. + */ + channel->onchannel_callback = + hv_cb_utils[channel->util_index].callback; + (hv_cb_utils[channel->util_index].callback)(channel); + return; + } + } + + buflen = PAGE_SIZE; + buf = kmalloc(buflen, GFP_ATOMIC); + + vmbus_recvpacket(channel, buf, buflen, &recvlen, &requestid); + + if (recvlen > 0) { + icmsghdrp = (struct icmsg_hdr *)&buf[ + sizeof(struct vmbuspipe_hdr)]; + + prep_negotiate_resp(icmsghdrp, negop, buf); + + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION + | ICMSGHDRFLAG_RESPONSE; + + vmbus_sendpacket(channel, buf, + recvlen, requestid, + VM_PKT_DATA_INBAND, 0); + } + + kfree(buf); +} +EXPORT_SYMBOL(chn_cb_negotiate); + +/* + * Function table used for message responses for non IDE/SCSI/Network type + * messages. (Such as KVP/Shutdown etc) + */ +struct hyperv_service_callback hv_cb_utils[MAX_MSG_TYPES] = { + /* 0E0B6031-5213-4934-818B-38D90CED39DB */ + /* Shutdown */ + { + .msg_type = HV_SHUTDOWN_MSG, + .data = { + 0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, + 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB + }, + .log_msg = "Shutdown channel functionality initialized" + }, + + /* {9527E630-D0AE-497b-ADCE-E80AB0175CAF} */ + /* TimeSync */ + { + .msg_type = HV_TIMESYNC_MSG, + .data = { + 0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, + 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf + }, + .log_msg = "Timesync channel functionality initialized" + }, + /* {57164f39-9115-4e78-ab55-382f3bd5422d} */ + /* Heartbeat */ + { + .msg_type = HV_HEARTBEAT_MSG, + .data = { + 0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, + 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d + }, + .log_msg = "Heartbeat channel functionality initialized" + }, + /* {A9A0F4E7-5A45-4d96-B827-8A841E8C03E6} */ + /* KVP */ + { + .data = { + 0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, + 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6 + }, + .log_msg = "KVP channel functionality initialized" + }, +}; +EXPORT_SYMBOL(hv_cb_utils); + +/* + * alloc_channel - Allocate and initialize a vmbus channel object + */ +static struct vmbus_channel *alloc_channel(void) +{ + struct vmbus_channel *channel; + + channel = kzalloc(sizeof(*channel), GFP_ATOMIC); + if (!channel) + return NULL; + + spin_lock_init(&channel->inbound_lock); + + channel->controlwq = create_workqueue("hv_vmbus_ctl"); + if (!channel->controlwq) { + kfree(channel); + return NULL; + } + + return channel; +} + +/* + * release_hannel - Release the vmbus channel object itself + */ +static void release_channel(struct work_struct *work) +{ + struct vmbus_channel *channel = container_of(work, + struct vmbus_channel, + work); + + destroy_workqueue(channel->controlwq); + + kfree(channel); +} + +/* + * free_channel - Release the resources used by the vmbus channel object + */ +void free_channel(struct vmbus_channel *channel) +{ + + /* + * We have to release the channel's workqueue/thread in the vmbus's + * workqueue/thread context + * ie we can't destroy ourselves. + */ + INIT_WORK(&channel->work, release_channel); + queue_work(vmbus_connection.work_queue, &channel->work); +} + + + +/* + * vmbus_process_rescind_offer - + * Rescind the offer by initiating a device removal + */ +static void vmbus_process_rescind_offer(struct work_struct *work) +{ + struct vmbus_channel *channel = container_of(work, + struct vmbus_channel, + work); + + vmbus_child_device_unregister(channel->device_obj); +} + +/* + * vmbus_process_offer - Process the offer by creating a channel/device + * associated with this offer + */ +static void vmbus_process_offer(struct work_struct *work) +{ + struct vmbus_channel *newchannel = container_of(work, + struct vmbus_channel, + work); + struct vmbus_channel *channel; + bool fnew = true; + int ret; + int cnt; + unsigned long flags; + + /* The next possible work is rescind handling */ + INIT_WORK(&newchannel->work, vmbus_process_rescind_offer); + + /* Make sure this is a new offer */ + spin_lock_irqsave(&vmbus_connection.channel_lock, flags); + + list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { + if (!memcmp(&channel->offermsg.offer.if_type, + &newchannel->offermsg.offer.if_type, + sizeof(struct hv_guid)) && + !memcmp(&channel->offermsg.offer.if_instance, + &newchannel->offermsg.offer.if_instance, + sizeof(struct hv_guid))) { + fnew = false; + break; + } + } + + if (fnew) + list_add_tail(&newchannel->listentry, + &vmbus_connection.chn_list); + + spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); + + if (!fnew) { + free_channel(newchannel); + return; + } + + /* + * Start the process of binding this offer to the driver + * We need to set the DeviceObject field before calling + * vmbus_child_dev_add() + */ + newchannel->device_obj = vmbus_child_device_create( + &newchannel->offermsg.offer.if_type, + &newchannel->offermsg.offer.if_instance, + newchannel); + + /* + * Add the new device to the bus. This will kick off device-driver + * binding which eventually invokes the device driver's AddDevice() + * method. + */ + ret = vmbus_child_device_register(newchannel->device_obj); + if (ret != 0) { + pr_err("unable to add child device object (relid %d)\n", + newchannel->offermsg.child_relid); + + spin_lock_irqsave(&vmbus_connection.channel_lock, flags); + list_del(&newchannel->listentry); + spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); + + free_channel(newchannel); + } else { + /* + * This state is used to indicate a successful open + * so that when we do close the channel normally, we + * can cleanup properly + */ + newchannel->state = CHANNEL_OPEN_STATE; + newchannel->util_index = -1; /* Invalid index */ + + /* Open IC channels */ + for (cnt = 0; cnt < MAX_MSG_TYPES; cnt++) { + if (memcmp(&newchannel->offermsg.offer.if_type, + &hv_cb_utils[cnt].data, + sizeof(struct hv_guid)) == 0 && + vmbus_open(newchannel, 2 * PAGE_SIZE, + 2 * PAGE_SIZE, NULL, 0, + chn_cb_negotiate, + newchannel) == 0) { + hv_cb_utils[cnt].channel = newchannel; + newchannel->util_index = cnt; + + pr_info("%s\n", hv_cb_utils[cnt].log_msg); + + } + } + } +} + +/* + * vmbus_onoffer - Handler for channel offers from vmbus in parent partition. + * + * We ignore all offers except network and storage offers. For each network and + * storage offers, we create a channel object and queue a work item to the + * channel object to process the offer synchronously + */ +static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) +{ + struct vmbus_channel_offer_channel *offer; + struct vmbus_channel *newchannel; + struct hv_guid *guidtype; + struct hv_guid *guidinstance; + int i; + int fsupported = 0; + + offer = (struct vmbus_channel_offer_channel *)hdr; + for (i = 0; i < MAX_NUM_DEVICE_CLASSES_SUPPORTED; i++) { + if (memcmp(&offer->offer.if_type, + &supported_device_classes[i], + sizeof(struct hv_guid)) == 0) { + fsupported = 1; + break; + } + } + + if (!fsupported) + return; + + guidtype = &offer->offer.if_type; + guidinstance = &offer->offer.if_instance; + + /* Allocate the channel object and save this offer. */ + newchannel = alloc_channel(); + if (!newchannel) { + pr_err("Unable to allocate channel object\n"); + return; + } + + memcpy(&newchannel->offermsg, offer, + sizeof(struct vmbus_channel_offer_channel)); + newchannel->monitor_grp = (u8)offer->monitorid / 32; + newchannel->monitor_bit = (u8)offer->monitorid % 32; + + INIT_WORK(&newchannel->work, vmbus_process_offer); + queue_work(newchannel->controlwq, &newchannel->work); +} + +/* + * vmbus_onoffer_rescind - Rescind offer handler. + * + * We queue a work item to process this offer synchronously + */ +static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) +{ + struct vmbus_channel_rescind_offer *rescind; + struct vmbus_channel *channel; + + rescind = (struct vmbus_channel_rescind_offer *)hdr; + channel = relid2channel(rescind->child_relid); + + if (channel == NULL) + /* Just return here, no channel found */ + return; + + /* work is initialized for vmbus_process_rescind_offer() from + * vmbus_process_offer() where the channel got created */ + queue_work(channel->controlwq, &channel->work); +} + +/* + * vmbus_onoffers_delivered - + * This is invoked when all offers have been delivered. + * + * Nothing to do here. + */ +static void vmbus_onoffers_delivered( + struct vmbus_channel_message_header *hdr) +{ +} + +/* + * vmbus_onopen_result - Open result handler. + * + * This is invoked when we received a response to our channel open request. + * Find the matching request, copy the response and signal the requesting + * thread. + */ +static void vmbus_onopen_result(struct vmbus_channel_message_header *hdr) +{ + struct vmbus_channel_open_result *result; + struct vmbus_channel_msginfo *msginfo; + struct vmbus_channel_message_header *requestheader; + struct vmbus_channel_open_channel *openmsg; + unsigned long flags; + + result = (struct vmbus_channel_open_result *)hdr; + + /* + * Find the open msg, copy the result and signal/unblock the wait event + */ + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + + list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, + msglistentry) { + requestheader = + (struct vmbus_channel_message_header *)msginfo->msg; + + if (requestheader->msgtype == CHANNELMSG_OPENCHANNEL) { + openmsg = + (struct vmbus_channel_open_channel *)msginfo->msg; + if (openmsg->child_relid == result->child_relid && + openmsg->openid == result->openid) { + memcpy(&msginfo->response.open_result, + result, + sizeof( + struct vmbus_channel_open_result)); + complete(&msginfo->waitevent); + break; + } + } + } + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); +} + +/* + * vmbus_ongpadl_created - GPADL created handler. + * + * This is invoked when we received a response to our gpadl create request. + * Find the matching request, copy the response and signal the requesting + * thread. + */ +static void vmbus_ongpadl_created(struct vmbus_channel_message_header *hdr) +{ + struct vmbus_channel_gpadl_created *gpadlcreated; + struct vmbus_channel_msginfo *msginfo; + struct vmbus_channel_message_header *requestheader; + struct vmbus_channel_gpadl_header *gpadlheader; + unsigned long flags; + + gpadlcreated = (struct vmbus_channel_gpadl_created *)hdr; + + /* + * Find the establish msg, copy the result and signal/unblock the wait + * event + */ + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + + list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, + msglistentry) { + requestheader = + (struct vmbus_channel_message_header *)msginfo->msg; + + if (requestheader->msgtype == CHANNELMSG_GPADL_HEADER) { + gpadlheader = + (struct vmbus_channel_gpadl_header *)requestheader; + + if ((gpadlcreated->child_relid == + gpadlheader->child_relid) && + (gpadlcreated->gpadl == gpadlheader->gpadl)) { + memcpy(&msginfo->response.gpadl_created, + gpadlcreated, + sizeof( + struct vmbus_channel_gpadl_created)); + complete(&msginfo->waitevent); + break; + } + } + } + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); +} + +/* + * vmbus_ongpadl_torndown - GPADL torndown handler. + * + * This is invoked when we received a response to our gpadl teardown request. + * Find the matching request, copy the response and signal the requesting + * thread. + */ +static void vmbus_ongpadl_torndown( + struct vmbus_channel_message_header *hdr) +{ + struct vmbus_channel_gpadl_torndown *gpadl_torndown; + struct vmbus_channel_msginfo *msginfo; + struct vmbus_channel_message_header *requestheader; + struct vmbus_channel_gpadl_teardown *gpadl_teardown; + unsigned long flags; + + gpadl_torndown = (struct vmbus_channel_gpadl_torndown *)hdr; + + /* + * Find the open msg, copy the result and signal/unblock the wait event + */ + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + + list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, + msglistentry) { + requestheader = + (struct vmbus_channel_message_header *)msginfo->msg; + + if (requestheader->msgtype == CHANNELMSG_GPADL_TEARDOWN) { + gpadl_teardown = + (struct vmbus_channel_gpadl_teardown *)requestheader; + + if (gpadl_torndown->gpadl == gpadl_teardown->gpadl) { + memcpy(&msginfo->response.gpadl_torndown, + gpadl_torndown, + sizeof( + struct vmbus_channel_gpadl_torndown)); + complete(&msginfo->waitevent); + break; + } + } + } + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); +} + +/* + * vmbus_onversion_response - Version response handler + * + * This is invoked when we received a response to our initiate contact request. + * Find the matching request, copy the response and signal the requesting + * thread. + */ +static void vmbus_onversion_response( + struct vmbus_channel_message_header *hdr) +{ + struct vmbus_channel_msginfo *msginfo; + struct vmbus_channel_message_header *requestheader; + struct vmbus_channel_initiate_contact *initiate; + struct vmbus_channel_version_response *version_response; + unsigned long flags; + + version_response = (struct vmbus_channel_version_response *)hdr; + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + + list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, + msglistentry) { + requestheader = + (struct vmbus_channel_message_header *)msginfo->msg; + + if (requestheader->msgtype == + CHANNELMSG_INITIATE_CONTACT) { + initiate = + (struct vmbus_channel_initiate_contact *)requestheader; + memcpy(&msginfo->response.version_response, + version_response, + sizeof(struct vmbus_channel_version_response)); + complete(&msginfo->waitevent); + } + } + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); +} + +/* Channel message dispatch table */ +static struct vmbus_channel_message_table_entry + channel_message_table[CHANNELMSG_COUNT] = { + {CHANNELMSG_INVALID, NULL}, + {CHANNELMSG_OFFERCHANNEL, vmbus_onoffer}, + {CHANNELMSG_RESCIND_CHANNELOFFER, vmbus_onoffer_rescind}, + {CHANNELMSG_REQUESTOFFERS, NULL}, + {CHANNELMSG_ALLOFFERS_DELIVERED, vmbus_onoffers_delivered}, + {CHANNELMSG_OPENCHANNEL, NULL}, + {CHANNELMSG_OPENCHANNEL_RESULT, vmbus_onopen_result}, + {CHANNELMSG_CLOSECHANNEL, NULL}, + {CHANNELMSG_GPADL_HEADER, NULL}, + {CHANNELMSG_GPADL_BODY, NULL}, + {CHANNELMSG_GPADL_CREATED, vmbus_ongpadl_created}, + {CHANNELMSG_GPADL_TEARDOWN, NULL}, + {CHANNELMSG_GPADL_TORNDOWN, vmbus_ongpadl_torndown}, + {CHANNELMSG_RELID_RELEASED, NULL}, + {CHANNELMSG_INITIATE_CONTACT, NULL}, + {CHANNELMSG_VERSION_RESPONSE, vmbus_onversion_response}, + {CHANNELMSG_UNLOAD, NULL}, +}; + +/* + * vmbus_onmessage - Handler for channel protocol messages. + * + * This is invoked in the vmbus worker thread context. + */ +void vmbus_onmessage(void *context) +{ + struct hv_message *msg = context; + struct vmbus_channel_message_header *hdr; + int size; + + hdr = (struct vmbus_channel_message_header *)msg->u.payload; + size = msg->header.payload_size; + + if (hdr->msgtype >= CHANNELMSG_COUNT) { + pr_err("Received invalid channel message type %d size %d\n", + hdr->msgtype, size); + print_hex_dump_bytes("", DUMP_PREFIX_NONE, + (unsigned char *)msg->u.payload, size); + return; + } + + if (channel_message_table[hdr->msgtype].message_handler) + channel_message_table[hdr->msgtype].message_handler(hdr); + else + pr_err("Unhandled channel message type %d\n", hdr->msgtype); +} + +/* + * vmbus_request_offers - Send a request to get all our pending offers. + */ +int vmbus_request_offers(void) +{ + struct vmbus_channel_message_header *msg; + struct vmbus_channel_msginfo *msginfo; + int ret, t; + + msginfo = kmalloc(sizeof(*msginfo) + + sizeof(struct vmbus_channel_message_header), + GFP_KERNEL); + if (!msginfo) + return -ENOMEM; + + init_completion(&msginfo->waitevent); + + msg = (struct vmbus_channel_message_header *)msginfo->msg; + + msg->msgtype = CHANNELMSG_REQUESTOFFERS; + + + ret = vmbus_post_msg(msg, + sizeof(struct vmbus_channel_message_header)); + if (ret != 0) { + pr_err("Unable to request offers - %d\n", ret); + + goto cleanup; + } + + t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); + if (t == 0) { + ret = -ETIMEDOUT; + goto cleanup; + } + + + +cleanup: + kfree(msginfo); + + return ret; +} + +/* eof */ diff --git a/drivers/staging/hv/connection.c b/drivers/staging/hv/connection.c new file mode 100644 index 0000000..7e15392 --- a/dev/null +++ b/drivers/staging/hv/connection.c @@ -0,0 +1,290 @@ +/* + * + * 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> + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> + +#include "hyperv.h" +#include "hyperv_vmbus.h" + + +struct vmbus_connection vmbus_connection = { + .conn_state = DISCONNECTED, + .next_gpadl_handle = ATOMIC_INIT(0xE1E10), +}; + +/* + * vmbus_connect - Sends a connect request on the partition service connection + */ +int vmbus_connect(void) +{ + int ret = 0; + int t; + struct vmbus_channel_msginfo *msginfo = NULL; + struct vmbus_channel_initiate_contact *msg; + unsigned long flags; + + /* Make sure we are not connecting or connected */ + if (vmbus_connection.conn_state != DISCONNECTED) + return -EISCONN; + + /* Initialize the vmbus connection */ + vmbus_connection.conn_state = CONNECTING; + vmbus_connection.work_queue = create_workqueue("hv_vmbus_con"); + if (!vmbus_connection.work_queue) { + ret = -ENOMEM; + goto cleanup; + } + + INIT_LIST_HEAD(&vmbus_connection.chn_msg_list); + spin_lock_init(&vmbus_connection.channelmsg_lock); + + INIT_LIST_HEAD(&vmbus_connection.chn_list); + spin_lock_init(&vmbus_connection.channel_lock); + + /* + * Setup the vmbus event connection for channel interrupt + * abstraction stuff + */ + vmbus_connection.int_page = + (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, 0); + if (vmbus_connection.int_page == NULL) { + ret = -ENOMEM; + goto cleanup; + } + + vmbus_connection.recv_int_page = vmbus_connection.int_page; + vmbus_connection.send_int_page = + (void *)((unsigned long)vmbus_connection.int_page + + (PAGE_SIZE >> 1)); + + /* + * Setup the monitor notification facility. The 1st page for + * parent->child and the 2nd page for child->parent + */ + vmbus_connection.monitor_pages = + (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 1); + if (vmbus_connection.monitor_pages == NULL) { + ret = -ENOMEM; + goto cleanup; + } + + msginfo = kzalloc(sizeof(*msginfo) + + sizeof(struct vmbus_channel_initiate_contact), + GFP_KERNEL); + if (msginfo == NULL) { + ret = -ENOMEM; + goto cleanup; + } + + init_completion(&msginfo->waitevent); + + msg = (struct vmbus_channel_initiate_contact *)msginfo->msg; + + msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT; + msg->vmbus_version_requested = VMBUS_REVISION_NUMBER; + msg->interrupt_page = virt_to_phys(vmbus_connection.int_page); + msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages); + msg->monitor_page2 = virt_to_phys( + (void *)((unsigned long)vmbus_connection.monitor_pages + + PAGE_SIZE)); + + /* + * Add to list before we send the request since we may + * receive the response before returning from this routine + */ + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_add_tail(&msginfo->msglistentry, + &vmbus_connection.chn_msg_list); + + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + + ret = vmbus_post_msg(msg, + sizeof(struct vmbus_channel_initiate_contact)); + if (ret != 0) { + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_del(&msginfo->msglistentry); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, + flags); + goto cleanup; + } + + /* Wait for the connection response */ + t = wait_for_completion_timeout(&msginfo->waitevent, HZ); + if (t == 0) { + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, + flags); + list_del(&msginfo->msglistentry); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, + flags); + ret = -ETIMEDOUT; + goto cleanup; + } + + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_del(&msginfo->msglistentry); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + + /* Check if successful */ + if (msginfo->response.version_response.version_supported) { + vmbus_connection.conn_state = CONNECTED; + } else { + pr_err("Unable to connect, " + "Version %d not supported by Hyper-V\n", + VMBUS_REVISION_NUMBER); + ret = -ECONNREFUSED; + goto cleanup; + } + + kfree(msginfo); + return 0; + +cleanup: + vmbus_connection.conn_state = DISCONNECTED; + + if (vmbus_connection.work_queue) + destroy_workqueue(vmbus_connection.work_queue); + + if (vmbus_connection.int_page) { + free_pages((unsigned long)vmbus_connection.int_page, 0); + vmbus_connection.int_page = NULL; + } + + if (vmbus_connection.monitor_pages) { + free_pages((unsigned long)vmbus_connection.monitor_pages, 1); + vmbus_connection.monitor_pages = NULL; + } + + kfree(msginfo); + + return ret; +} + + +/* + * relid2channel - Get the channel object given its + * child relative id (ie channel id) + */ +struct vmbus_channel *relid2channel(u32 relid) +{ + struct vmbus_channel *channel; + struct vmbus_channel *found_channel = NULL; + unsigned long flags; + + spin_lock_irqsave(&vmbus_connection.channel_lock, flags); + list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { + if (channel->offermsg.child_relid == relid) { + found_channel = channel; + break; + } + } + spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); + + return found_channel; +} + +/* + * process_chn_event - Process a channel event notification + */ +static void process_chn_event(u32 relid) +{ + struct vmbus_channel *channel; + + /* ASSERT(relId > 0); */ + + /* + * Find the channel based on this relid and invokes the + * channel callback to process the event + */ + channel = relid2channel(relid); + + if (channel) { + channel->onchannel_callback(channel->channel_callback_context); + } else { + pr_err("channel not found for relid - %u\n", relid); + } +} + +/* + * vmbus_on_event - Handler for events + */ +void vmbus_on_event(unsigned long data) +{ + u32 dword; + u32 maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5; + int bit; + u32 relid; + u32 *recv_int_page = vmbus_connection.recv_int_page; + + /* Check events */ + if (!recv_int_page) + return; + for (dword = 0; dword < maxdword; dword++) { + if (!recv_int_page[dword]) + continue; + for (bit = 0; bit < 32; bit++) { + if (sync_test_and_clear_bit(bit, (unsigned long *)&recv_int_page[dword])) { + relid = (dword << 5) + bit; + + if (relid == 0) { + /* + * Special case - vmbus + * channel protocol msg + */ + continue; + } + process_chn_event(relid); + } + } + } +} + +/* + * vmbus_post_msg - Send a msg on the vmbus's message connection + */ +int vmbus_post_msg(void *buffer, size_t buflen) +{ + union hv_connection_id conn_id; + + conn_id.asu32 = 0; + conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID; + return hv_post_message(conn_id, 1, buffer, buflen); +} + +/* + * vmbus_set_event - Send an event notification to the parent + */ +int vmbus_set_event(u32 child_relid) +{ + /* Each u32 represents 32 channels */ + sync_set_bit(child_relid & 31, + (unsigned long *)vmbus_connection.send_int_page + + (child_relid >> 5)); + + return hv_signal_event(); +} diff --git a/drivers/staging/hv/ext_utils.c b/drivers/staging/hv/ext_utils.c deleted file mode 100644 index a44cd1b..0000000 --- a/drivers/staging/hv/ext_utils.c +++ b/dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2010, 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/reboot.h> -#include "utils.h" - -void shutdown_linux_system() -{ - orderly_poweroff(false); -} diff --git a/drivers/staging/hv/hv.c b/drivers/staging/hv/hv.c new file mode 100644 index 0000000..824f816 --- a/dev/null +++ b/drivers/staging/hv/hv.c @@ -0,0 +1,438 @@ +/* + * 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> + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> + +#include "hyperv.h" +#include "hyperv_vmbus.h" + +/* The one and only */ +struct hv_context hv_context = { + .synic_initialized = false, + .hypercall_page = NULL, + .signal_event_param = NULL, + .signal_event_buffer = NULL, +}; + +/* + * query_hypervisor_presence + * - Query the cpuid for presence of windows hypervisor + */ +static int query_hypervisor_presence(void) +{ + unsigned int eax; + unsigned int ebx; + unsigned int ecx; + unsigned int edx; + unsigned int op; + + eax = 0; + ebx = 0; + ecx = 0; + edx = 0; + op = HVCPUID_VERSION_FEATURES; + cpuid(op, &eax, &ebx, &ecx, &edx); + + return ecx & HV_PRESENT_BIT; +} + +/* + * query_hypervisor_info - Get version info of the windows hypervisor + */ +static int query_hypervisor_info(void) +{ + unsigned int eax; + unsigned int ebx; + unsigned int ecx; + unsigned int edx; + unsigned int max_leaf; + unsigned int op; + + /* + * Its assumed that this is called after confirming that Viridian + * is present. Query id and revision. + */ + eax = 0; + ebx = 0; + ecx = 0; + edx = 0; + op = HVCPUID_VENDOR_MAXFUNCTION; + cpuid(op, &eax, &ebx, &ecx, &edx); + + max_leaf = eax; + + if (max_leaf >= HVCPUID_VERSION) { + eax = 0; + ebx = 0; + ecx = 0; + edx = 0; + op = HVCPUID_VERSION; + cpuid(op, &eax, &ebx, &ecx, &edx); + pr_info("Hyper-V Host OS Build:%d-%d.%d-%d-%d.%d\n", + eax, + ebx >> 16, + ebx & 0xFFFF, + ecx, + edx >> 24, + edx & 0xFFFFFF); + } + return max_leaf; +} + +/* + * do_hypercall- Invoke the specified hypercall + */ +static u64 do_hypercall(u64 control, void *input, void *output) +{ +#ifdef CONFIG_X86_64 + u64 hv_status = 0; + u64 input_address = (input) ? virt_to_phys(input) : 0; + u64 output_address = (output) ? virt_to_phys(output) : 0; + volatile void *hypercall_page = hv_context.hypercall_page; + + __asm__ __volatile__("mov %0, %%r8" : : "r" (output_address) : "r8"); + __asm__ __volatile__("call *%3" : "=a" (hv_status) : + "c" (control), "d" (input_address), + "m" (hypercall_page)); + + return hv_status; + +#else + + u32 control_hi = control >> 32; + u32 control_lo = control & 0xFFFFFFFF; + u32 hv_status_hi = 1; + u32 hv_status_lo = 1; + u64 input_address = (input) ? virt_to_phys(input) : 0; + u32 input_address_hi = input_address >> 32; + u32 input_address_lo = input_address & 0xFFFFFFFF; + u64 output_address = (output) ? virt_to_phys(output) : 0; + u32 output_address_hi = output_address >> 32; + u32 output_address_lo = output_address & 0xFFFFFFFF; + volatile void *hypercall_page = hv_context.hypercall_page; + + __asm__ __volatile__ ("call *%8" : "=d"(hv_status_hi), + "=a"(hv_status_lo) : "d" (control_hi), + "a" (control_lo), "b" (input_address_hi), + "c" (input_address_lo), "D"(output_address_hi), + "S"(output_address_lo), "m" (hypercall_page)); + + return hv_status_lo | ((u64)hv_status_hi << 32); +#endif /* !x86_64 */ +} + +/* + * hv_init - Main initialization routine. + * + * This routine must be called before any other routines in here are called + */ +int hv_init(void) +{ + int ret = 0; + int max_leaf; + union hv_x64_msr_hypercall_contents hypercall_msr; + void *virtaddr = NULL; + + memset(hv_context.synic_event_page, 0, sizeof(void *) * MAX_NUM_CPUS); + memset(hv_context.synic_message_page, 0, + sizeof(void *) * MAX_NUM_CPUS); + + if (!query_hypervisor_presence()) + goto cleanup; + + max_leaf = query_hypervisor_info(); + /* HvQueryHypervisorFeatures(maxLeaf); */ + + /* + * We only support running on top of Hyper-V + */ + rdmsrl(HV_X64_MSR_GUEST_OS_ID, hv_context.guestid); + + if (hv_context.guestid != 0) + goto cleanup; + + /* Write our OS info */ + wrmsrl(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID); + hv_context.guestid = HV_LINUX_GUEST_ID; + + /* See if the hypercall page is already set */ + rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); + + /* + * Allocate the hypercall page memory + * virtaddr = osd_page_alloc(1); + */ + virtaddr = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_EXEC); + + if (!virtaddr) + goto cleanup; + + hypercall_msr.enable = 1; + + hypercall_msr.guest_physical_address = vmalloc_to_pfn(virtaddr); + wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); + + /* Confirm that hypercall page did get setup. */ + hypercall_msr.as_uint64 = 0; + rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); + + if (!hypercall_msr.enable) + goto cleanup; + + hv_context.hypercall_page = virtaddr; + + /* Setup the global signal event param for the signal event hypercall */ + hv_context.signal_event_buffer = + kmalloc(sizeof(struct hv_input_signal_event_buffer), + GFP_KERNEL); + if (!hv_context.signal_event_buffer) + goto cleanup; + + hv_context.signal_event_param = + (struct hv_input_signal_event *) + (ALIGN((unsigned long) + hv_context.signal_event_buffer, + HV_HYPERCALL_PARAM_ALIGN)); + hv_context.signal_event_param->connectionid.asu32 = 0; + hv_context.signal_event_param->connectionid.u.id = + VMBUS_EVENT_CONNECTION_ID; + hv_context.signal_event_param->flag_number = 0; + hv_context.signal_event_param->rsvdz = 0; + + return ret; + +cleanup: + if (virtaddr) { + if (hypercall_msr.enable) { + hypercall_msr.as_uint64 = 0; + wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); + } + + vfree(virtaddr); + } + ret = -1; + return ret; +} + +/* + * hv_cleanup - Cleanup routine. + * + * This routine is called normally during driver unloading or exiting. + */ +void hv_cleanup(void) +{ + union hv_x64_msr_hypercall_contents hypercall_msr; + + kfree(hv_context.signal_event_buffer); + hv_context.signal_event_buffer = NULL; + hv_context.signal_event_param = NULL; + + if (hv_context.hypercall_page) { + hypercall_msr.as_uint64 = 0; + wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); + vfree(hv_context.hypercall_page); + hv_context.hypercall_page = NULL; + } +} + +/* + * hv_post_message - Post a message using the hypervisor message IPC. + * + * This involves a hypercall. + */ +u16 hv_post_message(union hv_connection_id connection_id, + enum hv_message_type message_type, + void *payload, size_t payload_size) +{ + struct aligned_input { + u64 alignment8; + struct hv_input_post_message msg; + }; + + struct hv_input_post_message *aligned_msg; + u16 status; + unsigned long addr; + + if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT) + return -EMSGSIZE; + + addr = (unsigned long)kmalloc(sizeof(struct aligned_input), GFP_ATOMIC); + if (!addr) + return -ENOMEM; + + aligned_msg = (struct hv_input_post_message *) + (ALIGN(addr, HV_HYPERCALL_PARAM_ALIGN)); + + aligned_msg->connectionid = connection_id; + aligned_msg->message_type = message_type; + aligned_msg->payload_size = payload_size; + memcpy((void *)aligned_msg->payload, payload, payload_size); + + status = do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL) + & 0xFFFF; + + kfree((void *)addr); + + return status; +} + + +/* + * hv_signal_event - + * Signal an event on the specified connection using the hypervisor event IPC. + * + * This involves a hypercall. + */ +u16 hv_signal_event(void) +{ + u16 status; + + status = do_hypercall(HVCALL_SIGNAL_EVENT, + hv_context.signal_event_param, + NULL) & 0xFFFF; + return status; +} + +/* + * hv_synic_init - Initialize the Synthethic Interrupt Controller. + * + * If it is already initialized by another entity (ie x2v shim), we need to + * retrieve the initialized message and event pages. Otherwise, we create and + * initialize the message and event pages. + */ +void hv_synic_init(void *irqarg) +{ + u64 version; + union hv_synic_simp simp; + union hv_synic_siefp siefp; + union hv_synic_sint shared_sint; + union hv_synic_scontrol sctrl; + + u32 irq_vector = *((u32 *)(irqarg)); + int cpu = smp_processor_id(); + + if (!hv_context.hypercall_page) + return; + + /* Check the version */ + rdmsrl(HV_X64_MSR_SVERSION, version); + + hv_context.synic_message_page[cpu] = + (void *)get_zeroed_page(GFP_ATOMIC); + + if (hv_context.synic_message_page[cpu] == NULL) { + pr_err("Unable to allocate SYNIC message page\n"); + goto cleanup; + } + + hv_context.synic_event_page[cpu] = + (void *)get_zeroed_page(GFP_ATOMIC); + + if (hv_context.synic_event_page[cpu] == NULL) { + pr_err("Unable to allocate SYNIC event page\n"); + goto cleanup; + } + + /* Setup the Synic's message page */ + rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64); + simp.simp_enabled = 1; + simp.base_simp_gpa = virt_to_phys(hv_context.synic_message_page[cpu]) + >> PAGE_SHIFT; + + wrmsrl(HV_X64_MSR_SIMP, simp.as_uint64); + + /* Setup the Synic's event page */ + rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); + siefp.siefp_enabled = 1; + siefp.base_siefp_gpa = virt_to_phys(hv_context.synic_event_page[cpu]) + >> PAGE_SHIFT; + + wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); + + /* Setup the shared SINT. */ + rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); + + shared_sint.as_uint64 = 0; + shared_sint.vector = irq_vector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */ + shared_sint.masked = false; + shared_sint.auto_eoi = true; + + wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); + + /* Enable the global synic bit */ + rdmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); + sctrl.enable = 1; + + wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); + + hv_context.synic_initialized = true; + return; + +cleanup: + if (hv_context.synic_event_page[cpu]) + free_page((unsigned long)hv_context.synic_event_page[cpu]); + + if (hv_context.synic_message_page[cpu]) + free_page((unsigned long)hv_context.synic_message_page[cpu]); + return; +} + +/* + * hv_synic_cleanup - Cleanup routine for hv_synic_init(). + */ +void hv_synic_cleanup(void *arg) +{ + union hv_synic_sint shared_sint; + union hv_synic_simp simp; + union hv_synic_siefp siefp; + int cpu = smp_processor_id(); + + if (!hv_context.synic_initialized) + return; + + rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); + + shared_sint.masked = 1; + + /* Need to correctly cleanup in the case of SMP!!! */ + /* Disable the interrupt */ + wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); + + rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64); + simp.simp_enabled = 0; + simp.base_simp_gpa = 0; + + wrmsrl(HV_X64_MSR_SIMP, simp.as_uint64); + + rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); + siefp.siefp_enabled = 0; + siefp.base_siefp_gpa = 0; + + wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); + + free_page((unsigned long)hv_context.synic_message_page[cpu]); + free_page((unsigned long)hv_context.synic_event_page[cpu]); +} diff --git a/drivers/staging/hv/hv_api.h b/drivers/staging/hv/hv_api.h deleted file mode 100644 index 9eb818e..0000000 --- a/drivers/staging/hv/hv_api.h +++ b/dev/null @@ -1,905 +0,0 @@ -/* - * - * 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> - * - */ -#ifndef __HV_API_H -#define __HV_API_H - - -/* Status codes for hypervisor operations. */ - -/* - * HV_STATUS_SUCCESS - * The specified hypercall succeeded - */ -#define HV_STATUS_SUCCESS ((u16)0x0000) - -/* - * HV_STATUS_INVALID_HYPERCALL_CODE - * The hypervisor does not support the operation because the specified - * hypercall code is not supported. - */ -#define HV_STATUS_INVALID_HYPERCALL_CODE ((u16)0x0002) - -/* - * HV_STATUS_INVALID_HYPERCALL_INPUT - * The hypervisor does not support the operation because the encoding for the - * hypercall input register is not supported. - */ -#define HV_STATUS_INVALID_HYPERCALL_INPUT ((u16)0x0003) - -/* - * HV_STATUS_INVALID_ALIGNMENT - * The hypervisor could not perform the operation beacuse a parameter has an - * invalid alignment. - */ -#define HV_STATUS_INVALID_ALIGNMENT ((u16)0x0004) - -/* - * HV_STATUS_INVALID_PARAMETER - * The hypervisor could not perform the operation beacuse an invalid parameter - * was specified. - */ -#define HV_STATUS_INVALID_PARAMETER ((u16)0x0005) - -/* - * HV_STATUS_ACCESS_DENIED - * Access to the specified object was denied. - */ -#define HV_STATUS_ACCESS_DENIED ((u16)0x0006) - -/* - * HV_STATUS_INVALID_PARTITION_STATE - * The hypervisor could not perform the operation because the partition is - * entering or in an invalid state. - */ -#define HV_STATUS_INVALID_PARTITION_STATE ((u16)0x0007) - -/* - * HV_STATUS_OPERATION_DENIED - * The operation is not allowed in the current state. - */ -#define HV_STATUS_OPERATION_DENIED ((u16)0x0008) - -/* - * HV_STATUS_UNKNOWN_PROPERTY - * The hypervisor does not recognize the specified partition property. - */ -#define HV_STATUS_UNKNOWN_PROPERTY ((u16)0x0009) - -/* - * HV_STATUS_PROPERTY_VALUE_OUT_OF_RANGE - * The specified value of a partition property is out of range or violates an - * invariant. - */ -#define HV_STATUS_PROPERTY_VALUE_OUT_OF_RANGE ((u16)0x000A) - -/* - * HV_STATUS_INSUFFICIENT_MEMORY - * There is not enough memory in the hypervisor pool to complete the operation. - */ -#define HV_STATUS_INSUFFICIENT_MEMORY ((u16)0x000B) - -/* - * HV_STATUS_PARTITION_TOO_DEEP - * The maximum partition depth has been exceeded for the partition hierarchy. - */ -#define HV_STATUS_PARTITION_TOO_DEEP ((u16)0x000C) - -/* - * HV_STATUS_INVALID_PARTITION_ID - * A partition with the specified partition Id does not exist. - */ -#define HV_STATUS_INVALID_PARTITION_ID ((u16)0x000D) - -/* - * HV_STATUS_INVALID_VP_INDEX - * The hypervisor could not perform the operation because the specified VP - * index is invalid. - */ -#define HV_STATUS_INVALID_VP_INDEX ((u16)0x000E) - -/* - * HV_STATUS_NOT_FOUND - * The iteration is complete; no addition items in the iteration could be - * found. - */ -#define HV_STATUS_NOT_FOUND ((u16)0x0010) - -/* - * HV_STATUS_INVALID_PORT_ID - * The hypervisor could not perform the operation because the specified port - * identifier is invalid. - */ -#define HV_STATUS_INVALID_PORT_ID ((u16)0x0011) - -/* - * HV_STATUS_INVALID_CONNECTION_ID - * The hypervisor could not perform the operation because the specified - * connection identifier is invalid. - */ -#define HV_STATUS_INVALID_CONNECTION_ID ((u16)0x0012) - -/* - * HV_STATUS_INSUFFICIENT_BUFFERS - * You did not supply enough message buffers to send a message. - */ -#define HV_STATUS_INSUFFICIENT_BUFFERS ((u16)0x0013) - -/* - * HV_STATUS_NOT_ACKNOWLEDGED - * The previous virtual interrupt has not been acknowledged. - */ -#define HV_STATUS_NOT_ACKNOWLEDGED ((u16)0x0014) - -/* - * HV_STATUS_INVALID_VP_STATE - * A virtual processor is not in the correct state for the performance of the - * indicated operation. - */ -#define HV_STATUS_INVALID_VP_STATE ((u16)0x0015) - -/* - * HV_STATUS_ACKNOWLEDGED - * The previous virtual interrupt has already been acknowledged. - */ -#define HV_STATUS_ACKNOWLEDGED ((u16)0x0016) - -/* - * HV_STATUS_INVALID_SAVE_RESTORE_STATE - * The indicated partition is not in a valid state for saving or restoring. - */ -#define HV_STATUS_INVALID_SAVE_RESTORE_STATE ((u16)0x0017) - -/* - * HV_STATUS_INVALID_SYNIC_STATE - * The hypervisor could not complete the operation because a required feature - * of the synthetic interrupt controller (SynIC) was disabled. - */ -#define HV_STATUS_INVALID_SYNIC_STATE ((u16)0x0018) - -/* - * HV_STATUS_OBJECT_IN_USE - * The hypervisor could not perform the operation because the object or value - * was either already in use or being used for a purpose that would not permit - * completing the operation. - */ -#define HV_STATUS_OBJECT_IN_USE ((u16)0x0019) - -/* - * HV_STATUS_INVALID_PROXIMITY_DOMAIN_INFO - * The proximity domain information is invalid. - */ -#define HV_STATUS_INVALID_PROXIMITY_DOMAIN_INFO ((u16)0x001A) - -/* - * HV_STATUS_NO_DATA - * An attempt to retrieve debugging data failed because none was available. - */ -#define HV_STATUS_NO_DATA ((u16)0x001B) - -/* - * HV_STATUS_INACTIVE - * The physical connection being used for debuggging has not recorded any - * receive activity since the last operation. - */ -#define HV_STATUS_INACTIVE ((u16)0x001C) - -/* - * HV_STATUS_NO_RESOURCES - * There are not enough resources to complete the operation. - */ -#define HV_STATUS_NO_RESOURCES ((u16)0x001D) - -/* - * HV_STATUS_FEATURE_UNAVAILABLE - * A hypervisor feature is not available to the user. - */ -#define HV_STATUS_FEATURE_UNAVAILABLE ((u16)0x001E) - -/* - * HV_STATUS_UNSUCCESSFUL - * {Operation Failed} The requested operation was unsuccessful. - */ -#define HV_STATUS_UNSUCCESSFUL ((u16)0x1001) - -/* - * HV_STATUS_INSUFFICIENT_BUFFER - * The specified buffer was too small to contain all of the requested data. - */ -#define HV_STATUS_INSUFFICIENT_BUFFER ((u16)0x1002) - -/* - * HV_STATUS_GPA_NOT_PRESENT - * The guest physical address is not currently associated with a system - * physical address. - */ -#define HV_STATUS_GPA_NOT_PRESENT ((u16)0x1003) - -/* - * HV_STATUS_GUEST_PAGE_FAULT - * The operation would have resulted in a page fault in the guest. - */ -#define HV_STATUS_GUEST_PAGE_FAULT ((u16)0x1004) - -/* - * HV_STATUS_RUNDOWN_DISABLED - * The operation cannot proceed as the rundown object was marked disabled. - */ -#define HV_STATUS_RUNDOWN_DISABLED ((u16)0x1005) - -/* - * HV_STATUS_KEY_ALREADY_EXISTS - * The entry cannot be added as another entry with the same key already exists. - */ -#define HV_STATUS_KEY_ALREADY_EXISTS ((u16)0x1006) - -/* - * HV_STATUS_GPA_INTERCEPT - * The operation resulted an intercept on a region of guest physical memory. - */ -#define HV_STATUS_GPA_INTERCEPT ((u16)0x1007) - -/* - * HV_STATUS_GUEST_GENERAL_PROTECTION_FAULT - * The operation would have resulted in a general protection fault in the - * guest. - */ -#define HV_STATUS_GUEST_GENERAL_PROTECTION_FAULT ((u16)0x1008) - -/* - * HV_STATUS_GUEST_STACK_FAULT - * The operation would have resulted in a stack fault in the guest. - */ -#define HV_STATUS_GUEST_STACK_FAULT ((u16)0x1009) - -/* - * HV_STATUS_GUEST_INVALID_OPCODE_FAULT - * The operation would have resulted in an invalid opcode fault in the guest. - */ -#define HV_STATUS_GUEST_INVALID_OPCODE_FAULT ((u16)0x100A) - -/* - * HV_STATUS_FINALIZE_INCOMPLETE - * The partition is not completely finalized. - */ -#define HV_STATUS_FINALIZE_INCOMPLETE ((u16)0x100B) - -/* - * HV_STATUS_GUEST_MACHINE_CHECK_ABORT - * The operation would have resulted in an machine check abort in the guest. - */ -#define HV_STATUS_GUEST_MACHINE_CHECK_ABORT ((u16)0x100C) - -/* - * HV_STATUS_ILLEGAL_OVERLAY_ACCESS - * An illegal access was attempted to an overlay page. - */ -#define HV_STATUS_ILLEGAL_OVERLAY_ACCESS ((u16)0x100D) - -/* - * HV_STATUS_INSUFFICIENT_SYSTEM_VA - * There is not enough system VA space available to satisfy the request, - */ -#define HV_STATUS_INSUFFICIENT_SYSTEM_VA ((u16)0x100E) - -/* - * HV_STATUS_VIRTUAL_ADDRESS_NOT_MAPPED - * The passed virtual address was not mapped in the hypervisor address space. - */ -#define HV_STATUS_VIRTUAL_ADDRESS_NOT_MAPPED ((u16)0x100F) - -/* - * HV_STATUS_NOT_IMPLEMENTED - * The requested operation is not implemented in this version of the - * hypervisor. - */ -#define HV_STATUS_NOT_IMPLEMENTED ((u16)0x1010) - -/* - * HV_STATUS_VMX_INSTRUCTION_FAILED - * The requested VMX instruction failed to complete successfully. - */ -#define HV_STATUS_VMX_INSTRUCTION_FAILED ((u16)0x1011) - -/* - * HV_STATUS_VMX_INSTRUCTION_FAILED_WITH_STATUS - * The requested VMX instruction failed to complete successfully indicating - * status. - */ -#define HV_STATUS_VMX_INSTRUCTION_FAILED_WITH_STATUS ((u16)0x1012) - -/* - * HV_STATUS_MSR_ACCESS_FAILED - * The requested access to the model specific register failed. - */ -#define HV_STATUS_MSR_ACCESS_FAILED ((u16)0x1013) - -/* - * HV_STATUS_CR_ACCESS_FAILED - * The requested access to the control register failed. - */ -#define HV_STATUS_CR_ACCESS_FAILED ((u16)0x1014) - -/* - * HV_STATUS_TIMEOUT - * The specified timeout expired before the operation completed. - */ -#define HV_STATUS_TIMEOUT ((u16)0x1016) - -/* - * HV_STATUS_MSR_INTERCEPT - * The requested access to the model specific register generated an intercept. - */ -#define HV_STATUS_MSR_INTERCEPT ((u16)0x1017) - -/* - * HV_STATUS_CPUID_INTERCEPT - * The CPUID instruction generated an intercept. - */ -#define HV_STATUS_CPUID_INTERCEPT ((u16)0x1018) - -/* - * HV_STATUS_REPEAT_INSTRUCTION - * The current instruction should be repeated and the instruction pointer not - * advanced. - */ -#define HV_STATUS_REPEAT_INSTRUCTION ((u16)0x1019) - -/* - * HV_STATUS_PAGE_PROTECTION_VIOLATION - * The current instruction should be repeated and the instruction pointer not - * advanced. - */ -#define HV_STATUS_PAGE_PROTECTION_VIOLATION ((u16)0x101A) - -/* - * HV_STATUS_PAGE_TABLE_INVALID - * The current instruction should be repeated and the instruction pointer not - * advanced. - */ -#define HV_STATUS_PAGE_TABLE_INVALID ((u16)0x101B) - -/* - * HV_STATUS_PAGE_NOT_PRESENT - * The current instruction should be repeated and the instruction pointer not - * advanced. - */ -#define HV_STATUS_PAGE_NOT_PRESENT ((u16)0x101C) - -/* - * HV_STATUS_IO_INTERCEPT - * The requested access to the I/O port generated an intercept. - */ -#define HV_STATUS_IO_INTERCEPT ((u16)0x101D) - -/* - * HV_STATUS_NOTHING_TO_DO - * There is nothing to do. - */ -#define HV_STATUS_NOTHING_TO_DO ((u16)0x101E) - -/* - * HV_STATUS_THREAD_TERMINATING - * The requested thread is terminating. - */ -#define HV_STATUS_THREAD_TERMINATING ((u16)0x101F) - -/* - * HV_STATUS_SECTION_ALREADY_CONSTRUCTED - * The specified section was already constructed. - */ -#define HV_STATUS_SECTION_ALREADY_CONSTRUCTED ((u16)0x1020) - -/* HV_STATUS_SECTION_NOT_ALREADY_CONSTRUCTED - * The specified section was not already constructed. - */ -#define HV_STATUS_SECTION_NOT_ALREADY_CONSTRUCTED ((u16)0x1021) - -/* - * HV_STATUS_PAGE_ALREADY_COMMITTED - * The specified virtual address was already backed by physical memory. - */ -#define HV_STATUS_PAGE_ALREADY_COMMITTED ((u16)0x1022) - -/* - * HV_STATUS_PAGE_NOT_ALREADY_COMMITTED - * The specified virtual address was not already backed by physical memory. - */ -#define HV_STATUS_PAGE_NOT_ALREADY_COMMITTED ((u16)0x1023) - -/* - * HV_STATUS_COMMITTED_PAGES_REMAIN - * Committed pages remain in the section. - */ -#define HV_STATUS_COMMITTED_PAGES_REMAIN ((u16)0x1024) - -/* - * HV_STATUS_NO_REMAINING_COMMITTED_PAGES - * No additional committed pages beyond the specified page exist in the - * section. - */ -#define HV_STATUS_NO_REMAINING_COMMITTED_PAGES ((u16)0x1025) - -/* - * HV_STATUS_INSUFFICIENT_COMPARTMENT_VA - * The VA space of the compartment is exhausted. - */ -#define HV_STATUS_INSUFFICIENT_COMPARTMENT_VA ((u16)0x1026) - -/* - * HV_STATUS_DEREF_SPA_LIST_FULL - * The SPA dereference list is full, and there are additional entries to be - * added to it. - */ -#define HV_STATUS_DEREF_SPA_LIST_FULL ((u16)0x1027) - -/* - * HV_STATUS_GPA_OUT_OF_RANGE - * The supplied GPA is out of range. - */ -#define HV_STATUS_GPA_OUT_OF_RANGE ((u16)0x1027) - -/* - * HV_STATUS_NONVOLATILE_XMM_STALE - * The XMM register that was being accessed is stale. - */ -#define HV_STATUS_NONVOLATILE_XMM_STALE ((u16)0x1028) - -/* HV_STATUS_UNSUPPORTED_PROCESSOR - * The hypervisor does not support the processors in this system. - */ -#define HV_STATUS_UNSUPPORTED_PROCESSOR ((u16)0x1029) - -/* - * HV_STATUS_INSUFFICIENT_CROM_SPACE - * Insufficient space existed for copying over the CROM contents. - */ -#define HV_STATUS_INSUFFICIENT_CROM_SPACE ((u16)0x2000) - -/* - * HV_STATUS_BAD_CROM_FORMAT - * The contents of the CROM failed validation attempts. - */ -#define HV_STATUS_BAD_CROM_FORMAT ((u16)0x2001) - -/* - * HV_STATUS_UNSUPPORTED_CROM_FORMAT - * The contents of the CROM contain contents the parser doesn't support. - */ -#define HV_STATUS_UNSUPPORTED_CROM_FORMAT ((u16)0x2002) - -/* - * HV_STATUS_UNSUPPORTED_CONTROLLER - * The register format of the OHCI controller specified for debugging is not - * supported. - */ -#define HV_STATUS_UNSUPPORTED_CONTROLLER ((u16)0x2003) - -/* - * HV_STATUS_CROM_TOO_LARGE - * The CROM contents were to large to copy over. - */ -#define HV_STATUS_CROM_TOO_LARGE ((u16)0x2004) - -/* - * HV_STATUS_CONTROLLER_IN_USE - * The OHCI controller specified for debugging cannot be used as it is already - * in use. - */ -#define HV_STATUS_CONTROLLER_IN_USE ((u16)0x2005) - - -/* - * The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent - * is set by CPUID(HvCpuIdFunctionVersionAndFeatures). - */ -enum hv_cpuid_function { - HvCpuIdFunctionVersionAndFeatures = 0x00000001, - HvCpuIdFunctionHvVendorAndMaxFunction = 0x40000000, - HvCpuIdFunctionHvInterface = 0x40000001, - - /* - * The remaining functions depend on the value of - * HvCpuIdFunctionInterface - */ - HvCpuIdFunctionMsHvVersion = 0x40000002, - HvCpuIdFunctionMsHvFeatures = 0x40000003, - HvCpuIdFunctionMsHvEnlightenmentInformation = 0x40000004, - HvCpuIdFunctionMsHvImplementationLimits = 0x40000005, -}; - -/* Define the virtual APIC registers */ -#define HV_X64_MSR_EOI (0x40000070) -#define HV_X64_MSR_ICR (0x40000071) -#define HV_X64_MSR_TPR (0x40000072) -#define HV_X64_MSR_APIC_ASSIST_PAGE (0x40000073) - -/* Define version of the synthetic interrupt controller. */ -#define HV_SYNIC_VERSION (1) - -/* Define synthetic interrupt controller model specific registers. */ -#define HV_X64_MSR_SCONTROL (0x40000080) -#define HV_X64_MSR_SVERSION (0x40000081) -#define HV_X64_MSR_SIEFP (0x40000082) -#define HV_X64_MSR_SIMP (0x40000083) -#define HV_X64_MSR_EOM (0x40000084) -#define HV_X64_MSR_SINT0 (0x40000090) -#define HV_X64_MSR_SINT1 (0x40000091) -#define HV_X64_MSR_SINT2 (0x40000092) -#define HV_X64_MSR_SINT3 (0x40000093) -#define HV_X64_MSR_SINT4 (0x40000094) -#define HV_X64_MSR_SINT5 (0x40000095) -#define HV_X64_MSR_SINT6 (0x40000096) -#define HV_X64_MSR_SINT7 (0x40000097) -#define HV_X64_MSR_SINT8 (0x40000098) -#define HV_X64_MSR_SINT9 (0x40000099) -#define HV_X64_MSR_SINT10 (0x4000009A) -#define HV_X64_MSR_SINT11 (0x4000009B) -#define HV_X64_MSR_SINT12 (0x4000009C) -#define HV_X64_MSR_SINT13 (0x4000009D) -#define HV_X64_MSR_SINT14 (0x4000009E) -#define HV_X64_MSR_SINT15 (0x4000009F) - -/* Define the expected SynIC version. */ -#define HV_SYNIC_VERSION_1 (0x1) - -/* Define synthetic interrupt controller message constants. */ -#define HV_MESSAGE_SIZE (256) -#define HV_MESSAGE_PAYLOAD_BYTE_COUNT (240) -#define HV_MESSAGE_PAYLOAD_QWORD_COUNT (30) -#define HV_ANY_VP (0xFFFFFFFF) - -/* Define synthetic interrupt controller flag constants. */ -#define HV_EVENT_FLAGS_COUNT (256 * 8) -#define HV_EVENT_FLAGS_BYTE_COUNT (256) -#define HV_EVENT_FLAGS_DWORD_COUNT (256 / sizeof(u32)) - -/* Define hypervisor message types. */ -enum hv_message_type { - HvMessageTypeNone = 0x00000000, - - /* Memory access messages. */ - HvMessageTypeUnmappedGpa = 0x80000000, - HvMessageTypeGpaIntercept = 0x80000001, - - /* Timer notification messages. */ - HvMessageTimerExpired = 0x80000010, - - /* Error messages. */ - HvMessageTypeInvalidVpRegisterValue = 0x80000020, - HvMessageTypeUnrecoverableException = 0x80000021, - HvMessageTypeUnsupportedFeature = 0x80000022, - - /* Trace buffer complete messages. */ - HvMessageTypeEventLogBufferComplete = 0x80000040, - - /* Platform-specific processor intercept messages. */ - HvMessageTypeX64IoPortIntercept = 0x80010000, - HvMessageTypeX64MsrIntercept = 0x80010001, - HvMessageTypeX64CpuidIntercept = 0x80010002, - HvMessageTypeX64ExceptionIntercept = 0x80010003, - HvMessageTypeX64ApicEoi = 0x80010004, - HvMessageTypeX64LegacyFpError = 0x80010005 -}; - -/* Define the number of synthetic interrupt sources. */ -#define HV_SYNIC_SINT_COUNT (16) -#define HV_SYNIC_STIMER_COUNT (4) - -/* Define invalid partition identifier. */ -#define HV_PARTITION_ID_INVALID ((u64)0x0) - -/* Define connection identifier type. */ -union hv_connection_id { - u32 Asu32; - struct { - u32 Id:24; - u32 Reserved:8; - } u; -}; - -/* Define port identifier type. */ -union hv_port_id { - u32 Asu32; - struct { - u32 Id:24; - u32 Reserved:8; - } u ; -}; - -/* Define port type. */ -enum hv_port_type { - HvPortTypeMessage = 1, - HvPortTypeEvent = 2, - HvPortTypeMonitor = 3 -}; - -/* Define port information structure. */ -struct hv_port_info { - enum hv_port_type PortType; - u32 Padding; - union { - struct { - u32 TargetSint; - u32 TargetVp; - u64 RsvdZ; - } MessagePortInfo; - struct { - u32 TargetSint; - u32 TargetVp; - u16 BaseFlagNumber; - u16 FlagCount; - u32 RsvdZ; - } EventPortInfo; - struct { - u64 MonitorAddress; - u64 RsvdZ; - } MonitorPortInfo; - }; -}; - -struct hv_connection_info { - enum hv_port_type PortType; - u32 Padding; - union { - struct { - u64 RsvdZ; - } MessageConnectionInfo; - struct { - u64 RsvdZ; - } EventConnectionInfo; - struct { - u64 MonitorAddress; - } MonitorConnectionInfo; - }; -}; - -/* Define synthetic interrupt controller message flags. */ -union hv_message_flags { - u8 Asu8; - struct { - u8 MessagePending:1; - u8 Reserved:7; - }; -}; - -/* Define synthetic interrupt controller message header. */ -struct hv_message_header { - enum hv_message_type MessageType; - u8 PayloadSize; - union hv_message_flags MessageFlags; - u8 Reserved[2]; - union { - u64 Sender; - union hv_port_id Port; - }; -}; - -/* Define timer message payload structure. */ -struct hv_timer_message_payload { - u32 TimerIndex; - u32 Reserved; - u64 ExpirationTime; /* When the timer expired */ - u64 DeliveryTime; /* When the message was delivered */ -}; - -/* Define synthetic interrupt controller message format. */ -struct hv_message { - struct hv_message_header Header; - union { - u64 Payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; - } u ; -}; - -/* Define the number of message buffers associated with each port. */ -#define HV_PORT_MESSAGE_BUFFER_COUNT (16) - -/* Define the synthetic interrupt message page layout. */ -struct hv_message_page { - struct hv_message SintMessage[HV_SYNIC_SINT_COUNT]; -}; - -/* Define the synthetic interrupt controller event flags format. */ -union hv_synic_event_flags { - u8 Flags8[HV_EVENT_FLAGS_BYTE_COUNT]; - u32 Flags32[HV_EVENT_FLAGS_DWORD_COUNT]; -}; - -/* Define the synthetic interrupt flags page layout. */ -struct hv_synic_event_flags_page { - union hv_synic_event_flags SintEventFlags[HV_SYNIC_SINT_COUNT]; -}; - -/* Define SynIC control register. */ -union hv_synic_scontrol { - u64 AsUINT64; - struct { - u64 Enable:1; - u64 Reserved:63; - }; -}; - -/* Define synthetic interrupt source. */ -union hv_synic_sint { - u64 AsUINT64; - struct { - u64 Vector:8; - u64 Reserved1:8; - u64 Masked:1; - u64 AutoEoi:1; - u64 Reserved2:46; - }; -}; - -/* Define the format of the SIMP register */ -union hv_synic_simp { - u64 AsUINT64; - struct { - u64 SimpEnabled:1; - u64 Preserved:11; - u64 BaseSimpGpa:52; - }; -}; - -/* Define the format of the SIEFP register */ -union hv_synic_siefp { - u64 AsUINT64; - struct { - u64 SiefpEnabled:1; - u64 Preserved:11; - u64 BaseSiefpGpa:52; - }; -}; - -/* Definitions for the monitored notification facility */ -union hv_monitor_trigger_group { - u64 AsUINT64; - struct { - u32 Pending; - u32 Armed; - }; -}; - -struct hv_monitor_parameter { - union hv_connection_id ConnectionId; - u16 FlagNumber; - u16 RsvdZ; -}; - -union hv_monitor_trigger_state { - u32 Asu32; - - struct { - u32 GroupEnable:4; - u32 RsvdZ:28; - }; -}; - -/* struct hv_monitor_page Layout */ -/* ------------------------------------------------------ */ -/* | 0 | TriggerState (4 bytes) | Rsvd1 (4 bytes) | */ -/* | 8 | TriggerGroup[0] | */ -/* | 10 | TriggerGroup[1] | */ -/* | 18 | TriggerGroup[2] | */ -/* | 20 | TriggerGroup[3] | */ -/* | 28 | Rsvd2[0] | */ -/* | 30 | Rsvd2[1] | */ -/* | 38 | Rsvd2[2] | */ -/* | 40 | NextCheckTime[0][0] | NextCheckTime[0][1] | */ -/* | ... | */ -/* | 240 | Latency[0][0..3] | */ -/* | 340 | Rsvz3[0] | */ -/* | 440 | Parameter[0][0] | */ -/* | 448 | Parameter[0][1] | */ -/* | ... | */ -/* | 840 | Rsvd4[0] | */ -/* ------------------------------------------------------ */ -struct hv_monitor_page { - union hv_monitor_trigger_state TriggerState; - u32 RsvdZ1; - - union hv_monitor_trigger_group TriggerGroup[4]; - u64 RsvdZ2[3]; - - s32 NextCheckTime[4][32]; - - u16 Latency[4][32]; - u64 RsvdZ3[32]; - - struct hv_monitor_parameter Parameter[4][32]; - - u8 RsvdZ4[1984]; -}; - -/* Declare the various hypercall operations. */ -enum hv_call_code { - HvCallPostMessage = 0x005c, - HvCallSignalEvent = 0x005d, -}; - -/* Definition of the HvPostMessage hypercall input structure. */ -struct hv_input_post_message { - union hv_connection_id ConnectionId; - u32 Reserved; - enum hv_message_type MessageType; - u32 PayloadSize; - u64 Payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; -}; - -/* Definition of the HvSignalEvent hypercall input structure. */ -struct hv_input_signal_event { - union hv_connection_id ConnectionId; - u16 FlagNumber; - u16 RsvdZ; -}; - -/* - * Versioning definitions used for guests reporting themselves to the - * hypervisor, and visa versa. - */ - -/* Version info reported by guest OS's */ -enum hv_guest_os_vendor { - HvGuestOsVendorMicrosoft = 0x0001 -}; - -enum hv_guest_os_microsoft_ids { - HvGuestOsMicrosoftUndefined = 0x00, - HvGuestOsMicrosoftMSDOS = 0x01, - HvGuestOsMicrosoftWindows3x = 0x02, - HvGuestOsMicrosoftWindows9x = 0x03, - HvGuestOsMicrosoftWindowsNT = 0x04, - HvGuestOsMicrosoftWindowsCE = 0x05 -}; - -/* - * Declare the MSR used to identify the guest OS. - */ -#define HV_X64_MSR_GUEST_OS_ID 0x40000000 - -union hv_x64_msr_guest_os_id_contents { - u64 AsUINT64; - struct { - u64 BuildNumber:16; - u64 ServiceVersion:8; /* Service Pack, etc. */ - u64 MinorVersion:8; - u64 MajorVersion:8; - u64 OsId:8; /* enum hv_guest_os_microsoft_ids (if Vendor=MS) */ - u64 VendorId:16; /* enum hv_guest_os_vendor */ - }; -}; - -/* - * Declare the MSR used to setup pages used to communicate with the hypervisor. - */ -#define HV_X64_MSR_HYPERCALL 0x40000001 - -union hv_x64_msr_hypercall_contents { - u64 AsUINT64; - struct { - u64 Enable:1; - u64 Reserved:11; - u64 GuestPhysicalAddress:52; - }; -}; - -#endif diff --git a/drivers/staging/hv/hv_compat.h b/drivers/staging/hv/hv_compat.h new file mode 100644 index 0000000..16724ba --- a/dev/null +++ b/drivers/staging/hv/hv_compat.h @@ -0,0 +1,81 @@ + +#ifndef _HV_COMPAT_H +#define _HV_COMPAT_H + +#include <linux/rcupdate.h> +#include <linux/version.h> +#include <linux/netdevice.h> +#include <linux/inetdevice.h> +#include <net/arp.h> +#include <scsi/scsi.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_dbg.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_driver.h> +#include <scsi/scsi_eh.h> +#include <scsi/scsi_host.h> + +#define CN_KVP_IDX 0x9 + +#ifndef pr_warn +#define pr_warn(fmt, arg...) printk(KERN_WARNING fmt, ##arg) +#endif + + +#define DEF_SCSI_QCMD(func_name) \ + int func_name(struct scsi_cmnd *cmd, \ + void (*done)(struct scsi_cmnd *)) \ + { \ + int rc; \ + rc = func_name##_lck(cmd, done); \ + return rc; \ + } + +#define blk_queue_max_segments(a, b) + +#ifndef netdev_err +static inline void netdev_err(struct net_device *net, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vprintk(fmt, args); + va_end(args); +} + +#endif + +#ifndef netdev_dbg +#if defined(DEBUG) +#define netdev_dbg(dev, fmt, ...) netdev_err(dev, fmt, ...) +#else +#define netdev_dbg(__dev, format, args...) \ +({ \ + if (0) \ + netdev_err(__dev, format, ##args); \ + 0; \ +}) + +#endif +#endif + + +#if defined(RHEL_RELEASE_VERSION) && (RHEL_RELEASE_CODE == 1536) && \ +LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) +static inline void netif_notify_peers(struct net_device *net) +{ + struct in_device *idev; + + rcu_read_lock(); + if (((idev = __in_dev_get_rcu(net)) != NULL) && + idev->ifa_list != NULL) { + arp_send(ARPOP_REQUEST, ETH_P_ARP, + idev->ifa_list->ifa_address, net, + idev->ifa_list->ifa_address, NULL, + net->dev_addr, NULL); + } + rcu_read_unlock(); +} + +#endif +#endif diff --git a/drivers/staging/hv/hv_kvp.c b/drivers/staging/hv/hv_kvp.c new file mode 100644 index 0000000..13b0ecf --- a/dev/null +++ b/drivers/staging/hv/hv_kvp.c @@ -0,0 +1,334 @@ +/* + * An implementation of key value pair (KVP) functionality for Linux. + * + * + * Copyright (C) 2010, Novell, Inc. + * Author : K. Y. Srinivasan <ksrinivasan@novell.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/net.h> +#include <linux/nls.h> +#include <linux/connector.h> +#include <linux/workqueue.h> + +#include "hyperv.h" +#include "hv_kvp.h" + + + +/* + * Global state maintained for transaction that is being processed. + * Note that only one transaction can be active at any point in time. + * + * This state is set when we receive a request from the host; we + * cleanup this state when the transaction is completed - when we respond + * to the host with the key value. + */ + +static struct { + bool active; /* transaction status - active or not */ + int recv_len; /* number of bytes received. */ + struct vmbus_channel *recv_channel; /* chn we got the request */ + u64 recv_req_id; /* request ID. */ +} kvp_transaction; + +static int kvp_send_key(int index); + +static void kvp_respond_to_host(char *key, char *value, int error); +static void kvp_work_func(struct work_struct *dummy); +static void kvp_register(void); + +static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func); + +static struct cb_id kvp_id = { CN_KVP_IDX, CN_KVP_VAL }; +static const char kvp_name[] = "kvp_kernel_module"; +static int timeout_fired; +static u8 *recv_buffer; +/* + * Register the kernel component with the user-level daemon. + * As part of this registration, pass the LIC version number. + */ + +static void +kvp_register(void) +{ + + struct cn_msg *msg; + + msg = kzalloc(sizeof(*msg) + strlen(HV_DRV_VERSION) + 1 , GFP_ATOMIC); + + if (msg) { + msg->id.idx = CN_KVP_IDX; + msg->id.val = CN_KVP_VAL; + msg->seq = KVP_REGISTER; + strcpy(msg->data, HV_DRV_VERSION); + msg->len = strlen(HV_DRV_VERSION) + 1; + cn_netlink_send(msg, 0, GFP_ATOMIC); + kfree(msg); + } +} +static void +kvp_work_func(struct work_struct *dummy) +{ + /* + * If the timer fires, the user-mode component has not responded; + * process the pending transaction. + */ + kvp_respond_to_host("Unknown key", "Guest timed out", timeout_fired); + timeout_fired = 1; +} + +/* + * Callback when data is received from user mode. + */ + +static void +kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) +{ + struct hv_ku_msg *message; + + message = (struct hv_ku_msg *)msg->data; + if (msg->seq == KVP_REGISTER) { + pr_info("KVP: user-mode registering done.\n"); + kvp_register(); + } + + if (msg->seq == KVP_USER_SET) { + /* + * Complete the transaction by forwarding the key value + * to the host. But first, cancel the timeout. + */ + if (cancel_delayed_work_sync(&kvp_work)) + kvp_respond_to_host(message->kvp_key, + message->kvp_value, + !strlen(message->kvp_key)); + } +} + +static int +kvp_send_key(int index) +{ + struct cn_msg *msg; + + msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC); + + if (msg) { + msg->id.idx = CN_KVP_IDX; + msg->id.val = CN_KVP_VAL; + msg->seq = KVP_KERNEL_GET; + ((struct hv_ku_msg *)msg->data)->kvp_index = index; + msg->len = sizeof(struct hv_ku_msg); + cn_netlink_send(msg, 0, GFP_ATOMIC); + kfree(msg); + return 0; + } + return 1; +} + +/* + * Send a response back to the host. + */ + +static void +kvp_respond_to_host(char *key, char *value, int error) +{ + struct hv_kvp_msg *kvp_msg; + struct hv_kvp_msg_enumerate *kvp_data; + char *key_name; + struct icmsg_hdr *icmsghdrp; + int keylen, valuelen; + u32 buf_len; + struct vmbus_channel *channel; + u64 req_id; + + /* + * If a transaction is not active; log and return. + */ + + if (!kvp_transaction.active) { + /* + * This is a spurious call! + */ + pr_warn("KVP: Transaction not active\n"); + return; + } + /* + * Copy the global state for completing the transaction. Note that + * only one transaction can be active at a time. + */ + + buf_len = kvp_transaction.recv_len; + channel = kvp_transaction.recv_channel; + req_id = kvp_transaction.recv_req_id; + + icmsghdrp = (struct icmsg_hdr *) + &recv_buffer[sizeof(struct vmbuspipe_hdr)]; + kvp_msg = (struct hv_kvp_msg *) + &recv_buffer[sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + kvp_data = &kvp_msg->kvp_data; + key_name = key; + + /* + * If the error parameter is set, terminate the host's enumeration. + */ + if (error) { + /* + * We don't support this index or the we have timedout; + * terminate the host-side iteration by returning an error. + */ + icmsghdrp->status = HV_E_FAIL; + goto response_done; + } + + /* + * The windows host expects the key/value pair to be encoded + * in utf16. + */ + keylen = utf8s_to_utf16s(key_name, strlen(key_name), + (wchar_t *)kvp_data->data.key); + kvp_data->data.key_size = 2*(keylen + 1); /* utf16 encoding */ + valuelen = utf8s_to_utf16s(value, strlen(value), + (wchar_t *)kvp_data->data.value); + kvp_data->data.value_size = 2*(valuelen + 1); /* utf16 encoding */ + + kvp_data->data.value_type = REG_SZ; /* all our values are strings */ + icmsghdrp->status = HV_S_OK; + +response_done: + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; + + vmbus_sendpacket(channel, recv_buffer, buf_len, req_id, + VM_PKT_DATA_INBAND, 0); + + kvp_transaction.active = false; +} + +/* + * This callback is invoked when we get a KVP message from the host. + * The host ensures that only one KVP transaction can be active at a time. + * KVP implementation in Linux needs to forward the key to a user-mde + * component to retrive the corresponding value. Consequently, we cannot + * respond to the host in the conext of this callback. Since the host + * guarantees that at most only one transaction can be active at a time, + * we stash away the transaction state in a set of global variables. + */ + +void hv_kvp_onchannelcallback(void *context) +{ + struct vmbus_channel *channel = context; + u32 recvlen; + u64 requestid; + + struct hv_kvp_msg *kvp_msg; + struct hv_kvp_msg_enumerate *kvp_data; + + struct icmsg_hdr *icmsghdrp; + struct icmsg_negotiate *negop = NULL; + + + if (kvp_transaction.active) + return; + + + vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE, &recvlen, &requestid); + + if (recvlen > 0) { + icmsghdrp = (struct icmsg_hdr *)&recv_buffer[ + sizeof(struct vmbuspipe_hdr)]; + + if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { + prep_negotiate_resp(icmsghdrp, negop, recv_buffer); + } else { + kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ + sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + + kvp_data = &kvp_msg->kvp_data; + + /* + * We only support the "get" operation on + * "KVP_POOL_AUTO" pool. + */ + + if ((kvp_msg->kvp_hdr.pool != KVP_POOL_AUTO) || + (kvp_msg->kvp_hdr.operation != + KVP_OP_ENUMERATE)) { + icmsghdrp->status = HV_E_FAIL; + goto callback_done; + } + + /* + * Stash away this global state for completing the + * transaction; note transactions are serialized. + */ + kvp_transaction.recv_len = recvlen; + kvp_transaction.recv_channel = channel; + kvp_transaction.recv_req_id = requestid; + kvp_transaction.active = true; + + /* + * Get the information from the + * user-mode component. + * component. This transaction will be + * completed when we get the value from + * the user-mode component. + * Set a timeout to deal with + * user-mode not responding. + */ + kvp_send_key(kvp_data->index); + schedule_delayed_work(&kvp_work, 100); + + return; + + } + +callback_done: + + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION + | ICMSGHDRFLAG_RESPONSE; + + vmbus_sendpacket(channel, recv_buffer, + recvlen, requestid, + VM_PKT_DATA_INBAND, 0); + } + +} + +int +hv_kvp_init(void) +{ + int err; + + err = cn_add_callback(&kvp_id, kvp_name, kvp_cn_callback); + if (err) + return err; + recv_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!recv_buffer) + return -ENOMEM; + + return 0; +} + +void hv_kvp_deinit(void) +{ + cn_del_callback(&kvp_id); + cancel_delayed_work_sync(&kvp_work); + kfree(recv_buffer); +} diff --git a/drivers/staging/hv/hv_kvp.h b/drivers/staging/hv/hv_kvp.h new file mode 100644 index 0000000..8c402f3 --- a/dev/null +++ b/drivers/staging/hv/hv_kvp.h @@ -0,0 +1,184 @@ +/* + * An implementation of HyperV key value pair (KVP) functionality for Linux. + * + * + * Copyright (C) 2010, Novell, Inc. + * Author : K. Y. Srinivasan <ksrinivasan@novell.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#ifndef _KVP_H +#define _KVP_H_ + +/* + * Maximum value size - used for both key names and value data, and includes + * any applicable NULL terminators. + * + * Note: This limit is somewhat arbitrary, but falls easily within what is + * supported for all native guests (back to Win 2000) and what is reasonable + * for the IC KVP exchange functionality. Note that Windows Me/98/95 are + * limited to 255 character key names. + * + * MSDN recommends not storing data values larger than 2048 bytes in the + * registry. + * + * Note: This value is used in defining the KVP exchange message - this value + * cannot be modified without affecting the message size and compatibility. + */ + +/* + * bytes, including any null terminators + */ +#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE (2048) + + +/* + * Maximum key size - the registry limit for the length of an entry name + * is 256 characters, including the null terminator + */ + +#define HV_KVP_EXCHANGE_MAX_KEY_SIZE (512) + +/* + * In Linux, we implement the KVP functionality in two components: + * 1) The kernel component which is packaged as part of the hv_utils driver + * is responsible for communicating with the host and responsible for + * implementing the host/guest protocol. 2) A user level daemon that is + * responsible for data gathering. + * + * Host/Guest Protocol: The host iterates over an index and expects the guest + * to assign a key name to the index and also return the value corresponding to + * the key. The host will have atmost one KVP transaction outstanding at any + * given point in time. The host side iteration stops when the guest returns + * an error. Microsoft has specified the following mapping of key names to + * host specified index: + * + * Index Key Name + * 0 FullyQualifiedDomainName + * 1 IntegrationServicesVersion + * 2 NetworkAddressIPv4 + * 3 NetworkAddressIPv6 + * 4 OSBuildNumber + * 5 OSName + * 6 OSMajorVersion + * 7 OSMinorVersion + * 8 OSVersion + * 9 ProcessorArchitecture + * + * The Windows host expects the Key Name and Key Value to be encoded in utf16. + * + * Guest Kernel/KVP Daemon Protocol: As noted earlier, we implement all of the + * data gathering functionality in a user mode daemon. The user level daemon + * is also responsible for binding the key name to the index as well. The + * kernel and user-level daemon communicate using a connector channel. + * + * The user mode component first registers with the + * the kernel component. Subsequently, the kernel component requests, data + * for the specified keys. In response to this message the user mode component + * fills in the value corresponding to the specified key. We overload the + * sequence field in the cn_msg header to define our KVP message types. + * + * + * The kernel component simply acts as a conduit for communication between the + * Windows host and the user-level daemon. The kernel component passes up the + * index received from the Host to the user-level daemon. If the index is + * valid (supported), the corresponding key as well as its + * value (both are strings) is returned. If the index is invalid + * (not supported), a NULL key string is returned. + */ + +/* + * + * The following definitions are shared with the user-mode component; do not + * change any of this without making the corresponding changes in + * the KVP user-mode component. + */ + +#define CN_KVP_VAL 0x1 /* This supports queries from the kernel */ +#define CN_KVP_USER_VAL 0x2 /* This supports queries from the user */ + +enum hv_ku_op { + KVP_REGISTER = 0, /* Register the user mode component */ + KVP_KERNEL_GET, /* Kernel is requesting the value */ + KVP_KERNEL_SET, /* Kernel is providing the value */ + KVP_USER_GET, /* User is requesting the value */ + KVP_USER_SET /* User is providing the value */ +}; + +struct hv_ku_msg { + __u32 kvp_index; /* Key index */ + __u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */ + __u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */ +}; + + + + +#ifdef __KERNEL__ + +/* + * Registry value types. + */ + +#define REG_SZ 1 + +enum hv_kvp_exchg_op { + KVP_OP_GET = 0, + KVP_OP_SET, + KVP_OP_DELETE, + KVP_OP_ENUMERATE, + KVP_OP_COUNT /* Number of operations, must be last. */ +}; + +enum hv_kvp_exchg_pool { + KVP_POOL_EXTERNAL = 0, + KVP_POOL_GUEST, + KVP_POOL_AUTO, + KVP_POOL_AUTO_EXTERNAL, + KVP_POOL_AUTO_INTERNAL, + KVP_POOL_COUNT /* Number of pools, must be last. */ +}; + +struct hv_kvp_hdr { + u8 operation; + u8 pool; +}; + +struct hv_kvp_exchg_msg_value { + u32 value_type; + u32 key_size; + u32 value_size; + u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; + u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; +}; + +struct hv_kvp_msg_enumerate { + u32 index; + struct hv_kvp_exchg_msg_value data; +}; + +struct hv_kvp_msg { + struct hv_kvp_hdr kvp_hdr; + struct hv_kvp_msg_enumerate kvp_data; +}; + +int hv_kvp_init(void); +void hv_kvp_deinit(void); +void hv_kvp_onchannelcallback(void *); + +#endif /* __KERNEL__ */ +#endif /* _KVP_H */ + diff --git a/drivers/staging/hv/hv_mouse.c b/drivers/staging/hv/hv_mouse.c new file mode 100644 index 0000000..b191810 --- a/dev/null +++ b/drivers/staging/hv/hv_mouse.c @@ -0,0 +1,975 @@ +/* + * Copyright (c) 2009, Citrix Systems, Inc. + * Copyright (c) 2010, Microsoft Corporation. + * Copyright (c) 2011, Novell Inc. + * + * 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. + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/workqueue.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/input.h> +#include <linux/hid.h> +#include <linux/hiddev.h> +#include <linux/pci.h> +#include <linux/dmi.h> +#include <linux/delay.h> + +#include "hyperv.h" + + +/* + * Data types + */ +struct hv_input_dev_info { + unsigned short vendor; + unsigned short product; + unsigned short version; + char name[128]; +}; + +/* The maximum size of a synthetic input message. */ +#define SYNTHHID_MAX_INPUT_REPORT_SIZE 16 + +/* + * Current version + * + * History: + * Beta, RC < 2008/1/22 1,0 + * RC > 2008/1/22 2,0 + */ +#define SYNTHHID_INPUT_VERSION_MAJOR 2 +#define SYNTHHID_INPUT_VERSION_MINOR 0 +#define SYNTHHID_INPUT_VERSION (SYNTHHID_INPUT_VERSION_MINOR | \ + (SYNTHHID_INPUT_VERSION_MAJOR << 16)) + + +#pragma pack(push,1) +/* + * Message types in the synthetic input protocol + */ +enum synthhid_msg_type { + SynthHidProtocolRequest, + SynthHidProtocolResponse, + SynthHidInitialDeviceInfo, + SynthHidInitialDeviceInfoAck, + SynthHidInputReport, + SynthHidMax +}; + +/* + * Basic message structures. + */ +struct synthhid_msg_hdr { + enum synthhid_msg_type type; + u32 size; +}; + +struct synthhid_msg { + struct synthhid_msg_hdr header; + char data[1]; /* Enclosed message */ +}; + +union synthhid_version { + struct { + u16 minor_version; + u16 major_version; + }; + u32 version; +}; + +/* + * Protocol messages + */ +struct synthhid_protocol_request { + struct synthhid_msg_hdr header; + union synthhid_version version_requested; +}; + +struct synthhid_protocol_response { + struct synthhid_msg_hdr header; + union synthhid_version version_requested; + unsigned char approved; +}; + +struct synthhid_device_info { + struct synthhid_msg_hdr header; + struct hv_input_dev_info hid_dev_info; + struct hid_descriptor hid_descriptor; +}; + +struct synthhid_device_info_ack { + struct synthhid_msg_hdr header; + unsigned char reserved; +}; + +struct synthhid_input_report { + struct synthhid_msg_hdr header; + char buffer[1]; +}; + +#pragma pack(pop) + +#define INPUTVSC_SEND_RING_BUFFER_SIZE 10*PAGE_SIZE +#define INPUTVSC_RECV_RING_BUFFER_SIZE 10*PAGE_SIZE + +#define NBITS(x) (((x)/BITS_PER_LONG)+1) + +enum pipe_prot_msg_type { + PipeMessageInvalid = 0, + PipeMessageData, + PipeMessageMaximum +}; + + +struct pipe_prt_msg { + enum pipe_prot_msg_type type; + u32 size; + char data[1]; +}; + +/* + * Data types + */ +struct mousevsc_prt_msg { + enum pipe_prot_msg_type type; + u32 size; + union { + struct synthhid_protocol_request request; + struct synthhid_protocol_response response; + struct synthhid_device_info_ack ack; + }; +}; + +/* + * Represents an mousevsc device + */ +struct mousevsc_dev { + struct hv_device *device; + /* 0 indicates the device is being destroyed */ + atomic_t ref_count; + int num_outstanding_req; + unsigned char init_complete; + struct mousevsc_prt_msg protocol_req; + struct mousevsc_prt_msg protocol_resp; + /* Synchronize the request/response if needed */ + wait_queue_head_t protocol_wait_event; + wait_queue_head_t dev_info_wait_event; + int protocol_wait_condition; + int device_wait_condition; + int dev_info_status; + + struct hid_descriptor *hid_desc; + unsigned char *report_desc; + u32 report_desc_size; + struct hv_input_dev_info hid_dev_info; +}; + + +static const char *driver_name = "mousevsc"; + +/* {CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A} */ +static const struct hv_guid mouse_guid = { + .data = {0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, + 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A} +}; + +static void deviceinfo_callback(struct hv_device *dev, struct hv_input_dev_info *info); +static void inputreport_callback(struct hv_device *dev, void *packet, u32 len); +static void reportdesc_callback(struct hv_device *dev, void *packet, u32 len); + +static struct mousevsc_dev *alloc_input_device(struct hv_device *device) +{ + struct mousevsc_dev *input_dev; + + input_dev = kzalloc(sizeof(struct mousevsc_dev), GFP_KERNEL); + + if (!input_dev) + return NULL; + + /* + * Set to 2 to allow both inbound and outbound traffics + * (ie get_input_device() and must_get_input_device()) to proceed. + */ + atomic_cmpxchg(&input_dev->ref_count, 0, 2); + + input_dev->device = device; + device->ext = input_dev; + + return input_dev; +} + +static void free_input_device(struct mousevsc_dev *device) +{ + WARN_ON(atomic_read(&device->ref_count) == 0); + kfree(device); +} + +/* + * Get the inputdevice object if exists and its refcount > 1 + */ +static struct mousevsc_dev *get_input_device(struct hv_device *device) +{ + struct mousevsc_dev *input_dev; + + input_dev = (struct mousevsc_dev *)device->ext; + +/* + * FIXME + * This sure isn't a valid thing to print for debugging, no matter + * what the intention is... + * + * printk(KERN_ERR "-------------------------> REFCOUNT = %d", + * input_dev->ref_count); + */ + + if (input_dev && atomic_read(&input_dev->ref_count) > 1) + atomic_inc(&input_dev->ref_count); + else + input_dev = NULL; + + return input_dev; +} + +/* + * Get the inputdevice object iff exists and its refcount > 0 + */ +static struct mousevsc_dev *must_get_input_device(struct hv_device *device) +{ + struct mousevsc_dev *input_dev; + + input_dev = (struct mousevsc_dev *)device->ext; + + if (input_dev && atomic_read(&input_dev->ref_count)) + atomic_inc(&input_dev->ref_count); + else + input_dev = NULL; + + return input_dev; +} + +static void put_input_device(struct hv_device *device) +{ + struct mousevsc_dev *input_dev; + + input_dev = (struct mousevsc_dev *)device->ext; + + atomic_dec(&input_dev->ref_count); +} + +/* + * Drop ref count to 1 to effectively disable get_input_device() + */ +static struct mousevsc_dev *release_input_device(struct hv_device *device) +{ + struct mousevsc_dev *input_dev; + + input_dev = (struct mousevsc_dev *)device->ext; + + /* Busy wait until the ref drop to 2, then set it to 1 */ + while (atomic_cmpxchg(&input_dev->ref_count, 2, 1) != 2) + udelay(100); + + return input_dev; +} + +/* + * Drop ref count to 0. No one can use input_device object. + */ +static struct mousevsc_dev *final_release_input_device(struct hv_device *device) +{ + struct mousevsc_dev *input_dev; + + input_dev = (struct mousevsc_dev *)device->ext; + + /* Busy wait until the ref drop to 1, then set it to 0 */ + while (atomic_cmpxchg(&input_dev->ref_count, 1, 0) != 1) + udelay(100); + + device->ext = NULL; + return input_dev; +} + +static void mousevsc_on_send_completion(struct hv_device *device, + struct vmpacket_descriptor *packet) +{ + struct mousevsc_dev *input_dev; + void *request; + + input_dev = must_get_input_device(device); + if (!input_dev) { + pr_err("unable to get input device...device being destroyed?"); + return; + } + + request = (void *)(unsigned long)packet->trans_id; + + if (request == &input_dev->protocol_req) { + /* FIXME */ + /* Shouldn't we be doing something here? */ + } + + put_input_device(device); +} + +static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, + struct synthhid_device_info *device_info) +{ + int ret = 0; + struct hid_descriptor *desc; + struct mousevsc_prt_msg ack; + + /* Assume success for now */ + input_device->dev_info_status = 0; + + /* Save the device attr */ + memcpy(&input_device->hid_dev_info, &device_info->hid_dev_info, + sizeof(struct hv_input_dev_info)); + + /* Save the hid desc */ + desc = &device_info->hid_descriptor; + WARN_ON(desc->bLength > 0); + + input_device->hid_desc = kzalloc(desc->bLength, GFP_KERNEL); + + if (!input_device->hid_desc) { + pr_err("unable to allocate hid descriptor - size %d", desc->bLength); + goto Cleanup; + } + + memcpy(input_device->hid_desc, desc, desc->bLength); + + /* Save the report desc */ + input_device->report_desc_size = desc->desc[0].wDescriptorLength; + input_device->report_desc = kzalloc(input_device->report_desc_size, + GFP_KERNEL); + + if (!input_device->report_desc) { + pr_err("unable to allocate report descriptor - size %d", + input_device->report_desc_size); + goto Cleanup; + } + + memcpy(input_device->report_desc, + ((unsigned char *)desc) + desc->bLength, + desc->desc[0].wDescriptorLength); + + /* Send the ack */ + memset(&ack, 0, sizeof(struct mousevsc_prt_msg)); + + ack.type = PipeMessageData; + ack.size = sizeof(struct synthhid_device_info_ack); + + ack.ack.header.type = SynthHidInitialDeviceInfoAck; + ack.ack.header.size = 1; + ack.ack.reserved = 0; + + ret = vmbus_sendpacket(input_device->device->channel, + &ack, + sizeof(struct pipe_prt_msg) - sizeof(unsigned char) + + sizeof(struct synthhid_device_info_ack), + (unsigned long)&ack, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (ret != 0) { + pr_err("unable to send synthhid device info ack - ret %d", + ret); + goto Cleanup; + } + + input_device->device_wait_condition = 1; + wake_up(&input_device->dev_info_wait_event); + + return; + +Cleanup: + kfree(input_device->hid_desc); + input_device->hid_desc = NULL; + + kfree(input_device->report_desc); + input_device->report_desc = NULL; + + input_device->dev_info_status = -1; + input_device->device_wait_condition = 1; + wake_up(&input_device->dev_info_wait_event); +} + +static void mousevsc_on_receive_input_report(struct mousevsc_dev *input_device, + struct synthhid_input_report *input_report) +{ + struct hv_driver *input_drv; + + if (!input_device->init_complete) { + pr_info("Initialization incomplete...ignoring input_report msg"); + return; + } + + input_drv = drv_to_hv_drv(input_device->device->device.driver); + + inputreport_callback(input_device->device, + input_report->buffer, + input_report->header.size); +} + +static void mousevsc_on_receive(struct hv_device *device, + struct vmpacket_descriptor *packet) +{ + struct pipe_prt_msg *pipe_msg; + struct synthhid_msg *hid_msg; + struct mousevsc_dev *input_dev; + + input_dev = must_get_input_device(device); + if (!input_dev) { + pr_err("unable to get input device...device being destroyed?"); + return; + } + + pipe_msg = (struct pipe_prt_msg *)((unsigned long)packet + + (packet->offset8 << 3)); + + if (pipe_msg->type != PipeMessageData) { + pr_err("unknown pipe msg type - type %d len %d", + pipe_msg->type, pipe_msg->size); + put_input_device(device); + return ; + } + + hid_msg = (struct synthhid_msg *)&pipe_msg->data[0]; + + switch (hid_msg->header.type) { + case SynthHidProtocolResponse: + memcpy(&input_dev->protocol_resp, pipe_msg, + pipe_msg->size + sizeof(struct pipe_prt_msg) - + sizeof(unsigned char)); + input_dev->protocol_wait_condition = 1; + wake_up(&input_dev->protocol_wait_event); + break; + + case SynthHidInitialDeviceInfo: + WARN_ON(pipe_msg->size >= sizeof(struct hv_input_dev_info)); + + /* + * Parse out the device info into device attr, + * hid desc and report desc + */ + mousevsc_on_receive_device_info(input_dev, + (struct synthhid_device_info *)&pipe_msg->data[0]); + break; + case SynthHidInputReport: + mousevsc_on_receive_input_report(input_dev, + (struct synthhid_input_report *)&pipe_msg->data[0]); + + break; + default: + pr_err("unsupported hid msg type - type %d len %d", + hid_msg->header.type, hid_msg->header.size); + break; + } + + put_input_device(device); +} + +static void mousevsc_on_channel_callback(void *context) +{ + const int packetSize = 0x100; + int ret = 0; + struct hv_device *device = (struct hv_device *)context; + struct mousevsc_dev *input_dev; + + u32 bytes_recvd; + u64 req_id; + unsigned char packet[0x100]; + struct vmpacket_descriptor *desc; + unsigned char *buffer = packet; + int bufferlen = packetSize; + + input_dev = must_get_input_device(device); + + if (!input_dev) { + pr_err("unable to get input device...device being destroyed?"); + return; + } + + do { + ret = vmbus_recvpacket_raw(device->channel, buffer, + bufferlen, &bytes_recvd, &req_id); + + if (ret == 0) { + if (bytes_recvd > 0) { + desc = (struct vmpacket_descriptor *)buffer; + + switch (desc->type) { + case VM_PKT_COMP: + mousevsc_on_send_completion( + device, desc); + break; + + case VM_PKT_DATA_INBAND: + mousevsc_on_receive( + device, desc); + break; + + default: + pr_err("unhandled packet type %d, tid %llx len %d\n", + desc->type, + req_id, + bytes_recvd); + break; + } + + /* reset */ + if (bufferlen > packetSize) { + kfree(buffer); + + buffer = packet; + bufferlen = packetSize; + } + } else { + /* + * pr_debug("nothing else to read..."); + * reset + */ + if (bufferlen > packetSize) { + kfree(buffer); + + buffer = packet; + bufferlen = packetSize; + } + break; + } + } else if (ret == -2) { + /* Handle large packet */ + bufferlen = bytes_recvd; + buffer = kzalloc(bytes_recvd, GFP_KERNEL); + + if (buffer == NULL) { + buffer = packet; + bufferlen = packetSize; + + /* Try again next time around */ + pr_err("unable to allocate buffer of size %d!", + bytes_recvd); + break; + } + } + } while (1); + + put_input_device(device); + + return; +} + +static int mousevsc_connect_to_vsp(struct hv_device *device) +{ + int ret = 0; + struct mousevsc_dev *input_dev; + struct mousevsc_prt_msg *request; + struct mousevsc_prt_msg *response; + + input_dev = get_input_device(device); + + if (!input_dev) { + pr_err("unable to get input device...device being destroyed?"); + return -1; + } + + init_waitqueue_head(&input_dev->protocol_wait_event); + init_waitqueue_head(&input_dev->dev_info_wait_event); + + request = &input_dev->protocol_req; + + /* + * Now, initiate the vsc/vsp initialization protocol on the open channel + */ + memset(request, 0, sizeof(struct mousevsc_prt_msg)); + + request->type = PipeMessageData; + request->size = sizeof(struct synthhid_protocol_request); + + request->request.header.type = SynthHidProtocolRequest; + request->request.header.size = sizeof(unsigned long); + request->request.version_requested.version = SYNTHHID_INPUT_VERSION; + + pr_info("synthhid protocol request..."); + + ret = vmbus_sendpacket(device->channel, request, + sizeof(struct pipe_prt_msg) - + sizeof(unsigned char) + + sizeof(struct synthhid_protocol_request), + (unsigned long)request, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (ret != 0) { + pr_err("unable to send synthhid protocol request."); + goto Cleanup; + } + + input_dev->protocol_wait_condition = 0; + wait_event_timeout(input_dev->protocol_wait_event, + input_dev->protocol_wait_condition, msecs_to_jiffies(1000)); + if (input_dev->protocol_wait_condition == 0) { + ret = -ETIMEDOUT; + goto Cleanup; + } + + response = &input_dev->protocol_resp; + + if (!response->response.approved) { + pr_err("synthhid protocol request failed (version %d)", + SYNTHHID_INPUT_VERSION); + ret = -1; + goto Cleanup; + } + + input_dev->device_wait_condition = 0; + wait_event_timeout(input_dev->dev_info_wait_event, + input_dev->device_wait_condition, msecs_to_jiffies(1000)); + if (input_dev->device_wait_condition == 0) { + ret = -ETIMEDOUT; + goto Cleanup; + } + + /* + * We should have gotten the device attr, hid desc and report + * desc at this point + */ + if (!input_dev->dev_info_status) + pr_info("**** input channel up and running!! ****"); + else + ret = -1; + +Cleanup: + put_input_device(device); + + return ret; +} + +static int mousevsc_on_device_add(struct hv_device *device, + void *additional_info) +{ + int ret = 0; + struct mousevsc_dev *input_dev; + struct hv_driver *input_drv; + struct hv_input_dev_info dev_info; + + input_dev = alloc_input_device(device); + + if (!input_dev) { + ret = -1; + goto Cleanup; + } + + input_dev->init_complete = false; + + /* Open the channel */ + ret = vmbus_open(device->channel, + INPUTVSC_SEND_RING_BUFFER_SIZE, + INPUTVSC_RECV_RING_BUFFER_SIZE, + NULL, + 0, + mousevsc_on_channel_callback, + device + ); + + if (ret != 0) { + pr_err("unable to open channel: %d", ret); + free_input_device(input_dev); + return -1; + } + + pr_info("InputVsc channel open: %d", ret); + + ret = mousevsc_connect_to_vsp(device); + + if (ret != 0) { + pr_err("unable to connect channel: %d", ret); + + vmbus_close(device->channel); + free_input_device(input_dev); + return ret; + } + + input_drv = drv_to_hv_drv(input_dev->device->device.driver); + + dev_info.vendor = input_dev->hid_dev_info.vendor; + dev_info.product = input_dev->hid_dev_info.product; + dev_info.version = input_dev->hid_dev_info.version; + strcpy(dev_info.name, "Microsoft Vmbus HID-compliant Mouse"); + + /* Send the device info back up */ + deviceinfo_callback(device, &dev_info); + + /* Send the report desc back up */ + /* workaround SA-167 */ + if (input_dev->report_desc[14] == 0x25) + input_dev->report_desc[14] = 0x29; + + reportdesc_callback(device, input_dev->report_desc, + input_dev->report_desc_size); + + input_dev->init_complete = true; + +Cleanup: + return ret; +} + +static int mousevsc_on_device_remove(struct hv_device *device) +{ + struct mousevsc_dev *input_dev; + int ret = 0; + + pr_info("disabling input device (%p)...", + device->ext); + + input_dev = release_input_device(device); + + + /* + * At this point, all outbound traffic should be disable. We only + * allow inbound traffic (responses) to proceed + * + * so that outstanding requests can be completed. + */ + while (input_dev->num_outstanding_req) { + pr_info("waiting for %d requests to complete...", + input_dev->num_outstanding_req); + + udelay(100); + } + + pr_info("removing input device (%p)...", device->ext); + + input_dev = final_release_input_device(device); + + pr_info("input device (%p) safe to remove", input_dev); + + /* Close the channel */ + vmbus_close(device->channel); + + free_input_device(input_dev); + + return ret; +} + + +/* + * Data types + */ +struct input_device_context { + struct hv_device *device_ctx; + struct hid_device *hid_device; + struct hv_input_dev_info device_info; + int connected; +}; + + +static void deviceinfo_callback(struct hv_device *dev, struct hv_input_dev_info *info) +{ + struct input_device_context *input_device_ctx = + dev_get_drvdata(&dev->device); + + memcpy(&input_device_ctx->device_info, info, + sizeof(struct hv_input_dev_info)); + + DPRINT_INFO(INPUTVSC_DRV, "%s", __func__); +} + +static void inputreport_callback(struct hv_device *dev, void *packet, u32 len) +{ + int ret = 0; + + struct input_device_context *input_dev_ctx = + dev_get_drvdata(&dev->device); + + ret = hid_input_report(input_dev_ctx->hid_device, + HID_INPUT_REPORT, packet, len, 1); + + DPRINT_DBG(INPUTVSC_DRV, "hid_input_report (ret %d)", ret); +} + +static int mousevsc_hid_open(struct hid_device *hid) +{ + return 0; +} + +static void mousevsc_hid_close(struct hid_device *hid) +{ +} + +static int mousevsc_probe(struct hv_device *dev) +{ + int ret = 0; + + struct input_device_context *input_dev_ctx; + + input_dev_ctx = kmalloc(sizeof(struct input_device_context), + GFP_KERNEL); + + dev_set_drvdata(&dev->device, input_dev_ctx); + + /* Call to the vsc driver to add the device */ + ret = mousevsc_on_device_add(dev, NULL); + + if (ret != 0) { + DPRINT_ERR(INPUTVSC_DRV, "unable to add input vsc device"); + + return -1; + } + + return 0; +} + +static int mousevsc_remove(struct hv_device *dev) +{ + int ret = 0; + + struct input_device_context *input_dev_ctx; + + input_dev_ctx = kmalloc(sizeof(struct input_device_context), + GFP_KERNEL); + + dev_set_drvdata(&dev->device, input_dev_ctx); + + if (input_dev_ctx->connected) { + hidinput_disconnect(input_dev_ctx->hid_device); + input_dev_ctx->connected = 0; + } + + /* + * Call to the vsc driver to let it know that the device + * is being removed + */ + ret = mousevsc_on_device_remove(dev); + + if (ret != 0) { + DPRINT_ERR(INPUTVSC_DRV, + "unable to remove vsc device (ret %d)", ret); + } + + kfree(input_dev_ctx); + + return ret; +} + +static void reportdesc_callback(struct hv_device *dev, void *packet, u32 len) +{ + struct input_device_context *input_device_ctx = + dev_get_drvdata(&dev->device); + struct hid_device *hid_dev; + + /* hid_debug = -1; */ + hid_dev = kmalloc(sizeof(struct hid_device), GFP_KERNEL); + + if (hid_parse_report(hid_dev, packet, len)) { + DPRINT_INFO(INPUTVSC_DRV, "Unable to call hd_parse_report"); + return; + } + + if (hid_dev) { + DPRINT_INFO(INPUTVSC_DRV, "hid_device created"); + + hid_dev->ll_driver->open = mousevsc_hid_open; + hid_dev->ll_driver->close = mousevsc_hid_close; + + hid_dev->bus = BUS_VIRTUAL; + hid_dev->vendor = input_device_ctx->device_info.vendor; + hid_dev->product = input_device_ctx->device_info.product; + hid_dev->version = input_device_ctx->device_info.version; + hid_dev->dev = dev->device; + + sprintf(hid_dev->name, "%s", + input_device_ctx->device_info.name); + + /* + * HJ Do we want to call it with a 0 + */ + if (!hidinput_connect(hid_dev, 0)) { + hid_dev->claimed |= HID_CLAIMED_INPUT; + + input_device_ctx->connected = 1; + + DPRINT_INFO(INPUTVSC_DRV, + "HID device claimed by input\n"); + } + + if (!hid_dev->claimed) { + DPRINT_ERR(INPUTVSC_DRV, + "HID device not claimed by " + "input or hiddev\n"); + } + + input_device_ctx->hid_device = hid_dev; + } + + kfree(hid_dev); +} + + +static struct hv_driver mousevsc_drv = { + .probe = mousevsc_probe, + .remove = mousevsc_remove, +}; + +static void mousevsc_drv_exit(void) +{ + vmbus_child_driver_unregister(&mousevsc_drv.driver); +} + +static int __init mousevsc_init(void) +{ + struct hv_driver *drv = &mousevsc_drv; + + DPRINT_INFO(INPUTVSC_DRV, "Hyper-V Mouse driver initializing."); + + memcpy(&drv->dev_type, &mouse_guid, + sizeof(struct hv_guid)); + + drv->driver.name = driver_name; + + /* The driver belongs to vmbus */ + vmbus_child_driver_register(&drv->driver); + + return 0; +} + +static void __exit mousevsc_exit(void) +{ + mousevsc_drv_exit(); +} + +/* + * We don't want to automatically load this driver just yet, it's quite + * broken. It's safe if you want to load it yourself manually, but + * don't inflict it on unsuspecting users, that's just mean. + */ +#if 0 + +/* + * We use a PCI table to determine if we should autoload this driver This is + * needed by distro tools to determine if the hyperv drivers should be + * installed and/or configured. We don't do anything else with the table, but + * it needs to be present. + */ +const static struct pci_device_id microsoft_hv_pci_table[] = { + { PCI_DEVICE(0x1414, 0x5353) }, /* VGA compatible controller */ + { 0 } +}; +MODULE_DEVICE_TABLE(pci, microsoft_hv_pci_table); +#endif + +MODULE_LICENSE("GPL"); +MODULE_VERSION(HV_DRV_VERSION); +module_init(mousevsc_init); +module_exit(mousevsc_exit); + diff --git a/drivers/staging/hv/hv_timesource.c b/drivers/staging/hv/hv_timesource.c index d3be8af..0efb049 100644 --- a/drivers/staging/hv/hv_timesource.c +++ b/drivers/staging/hv/hv_timesource.c @@ -5,9 +5,9 @@ * Copyright (C) 2010, Novell, Inc. * Author : K. Y. Srinivasan <ksrinivasan@novell.com> * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of @@ -20,6 +20,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/version.h> #include <linux/clocksource.h> @@ -27,23 +28,11 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/dmi.h> +#include <asm/hyperv.h> +#include <asm/mshyperv.h> +#include <asm/hypervisor.h> #define HV_CLOCK_SHIFT 22 -/* - * HyperV defined synthetic CPUID leaves: - */ -#define HV_CPUID_SIGNATURE 0x40000000 -#define HV_CPUID_MIN 0x40000005 -#define HV_HYPERVISOR_PRESENT_BIT 0x80000000 -#define HV_CPUID_FEATURES 0x40000003 -#define HV_CPUID_RECOMMENDATIONS 0x40000004 - -/* - * HyperV defined synthetic MSRs - */ - -#define HV_X64_MSR_TIME_REF_COUNT 0x40000020 - static cycle_t read_hv_clock(struct clocksource *arg) { @@ -94,49 +83,16 @@ hv_timesource_pci_table[] __maybe_unused = { MODULE_DEVICE_TABLE(pci, hv_timesource_pci_table); -static int __init hv_detect_hyperv(void) -{ - u32 eax, ebx, ecx, edx; - char hyp_signature[13]; - - cpuid(1, &eax, &ebx, &ecx, &edx); - - if (!(ecx & HV_HYPERVISOR_PRESENT_BIT)) - return 1; - - cpuid(HV_CPUID_SIGNATURE, &eax, &ebx, &ecx, &edx); - *(u32 *)(hyp_signature + 0) = ebx; - *(u32 *)(hyp_signature + 4) = ecx; - *(u32 *)(hyp_signature + 8) = edx; - - if ((eax < HV_CPUID_MIN) || (memcmp("Microsoft Hv", hyp_signature, 12))) - return 1; - - /* - * Extract the features, recommendations etc. - */ - cpuid(HV_CPUID_FEATURES, &eax, &ebx, &ecx, &edx); - if (!(eax & 0x10)) { - printk(KERN_WARNING "HyperV Time Ref Counter not available!\n"); - return 1; - } - - cpuid(HV_CPUID_RECOMMENDATIONS, &eax, &ebx, &ecx, &edx); - printk(KERN_INFO "HyperV recommendations: %x\n", eax); - printk(KERN_INFO "HyperV spin count: %x\n", ebx); - return 0; -} - - static int __init init_hv_clocksource(void) { - if (hv_detect_hyperv()) + if ((x86_hyper != &x86_hyper_ms_hyperv) || + !(ms_hyperv.features & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE)) return -ENODEV; if (!dmi_check_system(hv_timesource_dmi_table)) return -ENODEV; - printk(KERN_INFO "Registering HyperV clock source\n"); + pr_info("Registering HyperV clock source\n"); return clocksource_register(&hyperv_cs); } diff --git a/drivers/staging/hv/hyperv_utils.c b/drivers/staging/hv/hv_util.c index fa78ba0..c164b54 100644 --- a/drivers/staging/hv/hyperv_utils.c +++ b/drivers/staging/hv/hv_util.c @@ -18,6 +18,8 @@ * Haiyang Zhang <haiyangz@microsoft.com> * Hank Janssen <hjanssen@microsoft.com> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> @@ -27,23 +29,17 @@ #include <linux/dmi.h> #include <linux/pci.h> -#include "logging.h" -#include "osd.h" -#include "vmbus.h" -#include "VmbusPacketFormat.h" -#include "VmbusChannelInterface.h" -#include "VersionInfo.h" -#include "Channel.h" -#include "VmbusPrivate.h" -#include "VmbusApi.h" -#include "utils.h" +#include "hyperv.h" +#include "hv_kvp.h" +static u8 *shut_txf_buf; +static u8 *time_txf_buf; +static u8 *hbeat_txf_buf; static void shutdown_onchannelcallback(void *context) { struct vmbus_channel *channel = context; - u8 *buf; - u32 buflen, recvlen; + u32 recvlen; u64 requestid; u8 execute_shutdown = false; @@ -52,24 +48,20 @@ static void shutdown_onchannelcallback(void *context) struct icmsg_hdr *icmsghdrp; struct icmsg_negotiate *negop = NULL; - buflen = PAGE_SIZE; - buf = kmalloc(buflen, GFP_ATOMIC); - - VmbusChannelRecvPacket(channel, buf, buflen, &recvlen, &requestid); + vmbus_recvpacket(channel, shut_txf_buf, + PAGE_SIZE, &recvlen, &requestid); if (recvlen > 0) { - DPRINT_DBG(VMBUS, "shutdown packet: len=%d, requestid=%lld", - recvlen, requestid); - - icmsghdrp = (struct icmsg_hdr *)&buf[ + icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[ sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - prep_negotiate_resp(icmsghdrp, negop, buf); + prep_negotiate_resp(icmsghdrp, negop, shut_txf_buf); } else { - shutdown_msg = (struct shutdown_msg_data *)&buf[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; + shutdown_msg = + (struct shutdown_msg_data *)&shut_txf_buf[ + sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; switch (shutdown_msg->flags) { case 0: @@ -77,29 +69,27 @@ static void shutdown_onchannelcallback(void *context) icmsghdrp->status = HV_S_OK; execute_shutdown = true; - DPRINT_INFO(VMBUS, "Shutdown request received -" - " gracefull shutdown initiated"); + pr_info("Shutdown request received -" + " graceful shutdown initiated\n"); break; default: icmsghdrp->status = HV_E_FAIL; execute_shutdown = false; - DPRINT_INFO(VMBUS, "Shutdown request received -" - " Invalid request"); + pr_info("Shutdown request received -" + " Invalid request\n"); break; - }; + } } icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; - VmbusChannelSendPacket(channel, buf, + vmbus_sendpacket(channel, shut_txf_buf, recvlen, requestid, - VmbusPacketTypeDataInBand, 0); + VM_PKT_DATA_INBAND, 0); } - kfree(buf); - if (execute_shutdown == true) orderly_poweroff(false); } @@ -150,28 +140,22 @@ static inline void adj_guesttime(u64 hosttime, u8 flags) static void timesync_onchannelcallback(void *context) { struct vmbus_channel *channel = context; - u8 *buf; - u32 buflen, recvlen; + u32 recvlen; u64 requestid; struct icmsg_hdr *icmsghdrp; struct ictimesync_data *timedatap; - buflen = PAGE_SIZE; - buf = kmalloc(buflen, GFP_ATOMIC); - - VmbusChannelRecvPacket(channel, buf, buflen, &recvlen, &requestid); + vmbus_recvpacket(channel, time_txf_buf, + PAGE_SIZE, &recvlen, &requestid); if (recvlen > 0) { - DPRINT_DBG(VMBUS, "timesync packet: recvlen=%d, requestid=%lld", - recvlen, requestid); - - icmsghdrp = (struct icmsg_hdr *)&buf[ + icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[ sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - prep_negotiate_resp(icmsghdrp, NULL, buf); + prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf); } else { - timedatap = (struct ictimesync_data *)&buf[ + timedatap = (struct ictimesync_data *)&time_txf_buf[ sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; adj_guesttime(timedatap->parenttime, timedatap->flags); @@ -180,12 +164,10 @@ static void timesync_onchannelcallback(void *context) icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; - VmbusChannelSendPacket(channel, buf, + vmbus_sendpacket(channel, time_txf_buf, recvlen, requestid, - VmbusPacketTypeDataInBand, 0); + VM_PKT_DATA_INBAND, 0); } - - kfree(buf); } /* @@ -196,36 +178,25 @@ static void timesync_onchannelcallback(void *context) static void heartbeat_onchannelcallback(void *context) { struct vmbus_channel *channel = context; - u8 *buf; - u32 buflen, recvlen; + u32 recvlen; u64 requestid; struct icmsg_hdr *icmsghdrp; struct heartbeat_msg_data *heartbeat_msg; - buflen = PAGE_SIZE; - buf = kmalloc(buflen, GFP_ATOMIC); - - VmbusChannelRecvPacket(channel, buf, buflen, &recvlen, &requestid); + vmbus_recvpacket(channel, hbeat_txf_buf, + PAGE_SIZE, &recvlen, &requestid); if (recvlen > 0) { - DPRINT_DBG(VMBUS, "heartbeat packet: len=%d, requestid=%lld", - recvlen, requestid); - - icmsghdrp = (struct icmsg_hdr *)&buf[ - sizeof(struct vmbuspipe_hdr)]; - - icmsghdrp = (struct icmsg_hdr *)&buf[ + icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[ sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - prep_negotiate_resp(icmsghdrp, NULL, buf); + prep_negotiate_resp(icmsghdrp, NULL, hbeat_txf_buf); } else { - heartbeat_msg = (struct heartbeat_msg_data *)&buf[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - - DPRINT_DBG(VMBUS, "heartbeat seq = %lld", - heartbeat_msg->seq_num); + heartbeat_msg = + (struct heartbeat_msg_data *)&hbeat_txf_buf[ + sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; heartbeat_msg->seq_num += 1; } @@ -233,12 +204,10 @@ static void heartbeat_onchannelcallback(void *context) icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; - VmbusChannelSendPacket(channel, buf, + vmbus_sendpacket(channel, hbeat_txf_buf, recvlen, requestid, - VmbusPacketTypeDataInBand, 0); + VM_PKT_DATA_INBAND, 0); } - - kfree(buf); } static const struct pci_device_id __initconst @@ -248,6 +217,7 @@ hv_utils_pci_table[] __maybe_unused = { }; MODULE_DEVICE_TABLE(pci, hv_utils_pci_table); + static const struct dmi_system_id __initconst hv_utils_dmi_table[] __maybe_unused = { { @@ -265,38 +235,67 @@ MODULE_DEVICE_TABLE(dmi, hv_utils_dmi_table); static int __init init_hyperv_utils(void) { - printk(KERN_INFO "Registering HyperV Utility Driver\n"); + pr_info("Registering HyperV Utility Driver\n"); + + if (hv_kvp_init()) + return -ENODEV; + + + if (!dmi_check_system(hv_utils_dmi_table)) + return -ENODEV; + + shut_txf_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + time_txf_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + hbeat_txf_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + + if (!shut_txf_buf || !time_txf_buf || !hbeat_txf_buf) { + pr_info("Unable to allocate memory for receive buffer\n"); + kfree(shut_txf_buf); + kfree(time_txf_buf); + kfree(hbeat_txf_buf); + return -ENOMEM; + } - hv_cb_utils[HV_SHUTDOWN_MSG].channel->OnChannelCallback = - &shutdown_onchannelcallback; hv_cb_utils[HV_SHUTDOWN_MSG].callback = &shutdown_onchannelcallback; - hv_cb_utils[HV_TIMESYNC_MSG].channel->OnChannelCallback = - ×ync_onchannelcallback; hv_cb_utils[HV_TIMESYNC_MSG].callback = ×ync_onchannelcallback; - hv_cb_utils[HV_HEARTBEAT_MSG].channel->OnChannelCallback = - &heartbeat_onchannelcallback; hv_cb_utils[HV_HEARTBEAT_MSG].callback = &heartbeat_onchannelcallback; + hv_cb_utils[HV_KVP_MSG].callback = &hv_kvp_onchannelcallback; + return 0; } static void exit_hyperv_utils(void) { - printk(KERN_INFO "De-Registered HyperV Utility Driver\n"); + pr_info("De-Registered HyperV Utility Driver\n"); + + if (hv_cb_utils[HV_SHUTDOWN_MSG].channel != NULL) + hv_cb_utils[HV_SHUTDOWN_MSG].channel->onchannel_callback = + &chn_cb_negotiate; + hv_cb_utils[HV_SHUTDOWN_MSG].callback = NULL; + + if (hv_cb_utils[HV_TIMESYNC_MSG].channel != NULL) + hv_cb_utils[HV_TIMESYNC_MSG].channel->onchannel_callback = + &chn_cb_negotiate; + hv_cb_utils[HV_TIMESYNC_MSG].callback = NULL; + + if (hv_cb_utils[HV_HEARTBEAT_MSG].channel != NULL) + hv_cb_utils[HV_HEARTBEAT_MSG].channel->onchannel_callback = + &chn_cb_negotiate; + hv_cb_utils[HV_HEARTBEAT_MSG].callback = NULL; - hv_cb_utils[HV_SHUTDOWN_MSG].channel->OnChannelCallback = - &chn_cb_negotiate; - hv_cb_utils[HV_SHUTDOWN_MSG].callback = &chn_cb_negotiate; + if (hv_cb_utils[HV_KVP_MSG].channel != NULL) + hv_cb_utils[HV_KVP_MSG].channel->onchannel_callback = + &chn_cb_negotiate; + hv_cb_utils[HV_KVP_MSG].callback = NULL; - hv_cb_utils[HV_TIMESYNC_MSG].channel->OnChannelCallback = - &chn_cb_negotiate; - hv_cb_utils[HV_TIMESYNC_MSG].callback = &chn_cb_negotiate; + hv_kvp_deinit(); - hv_cb_utils[HV_HEARTBEAT_MSG].channel->OnChannelCallback = - &chn_cb_negotiate; - hv_cb_utils[HV_HEARTBEAT_MSG].callback = &chn_cb_negotiate; + kfree(shut_txf_buf); + kfree(time_txf_buf); + kfree(hbeat_txf_buf); } module_init(init_hyperv_utils); diff --git a/drivers/staging/hv/hyperv.h b/drivers/staging/hv/hyperv.h new file mode 100644 index 0000000..8175b25 --- a/dev/null +++ b/drivers/staging/hv/hyperv.h @@ -0,0 +1,953 @@ +/* + * + * Copyright (c) 2011, 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> + * K. Y. Srinivasan <kys@microsoft.com> + * + */ + +#ifndef _HYPERV_H +#define _HYPERV_H + +#include <linux/scatterlist.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/workqueue.h> +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/version.h> + + +#include <asm/hyperv.h> + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) +#include "hv_compat.h" +#endif + +struct hv_guid { + unsigned char data[16]; +}; + +#define MAX_PAGE_BUFFER_COUNT 16 +#define MAX_MULTIPAGE_BUFFER_COUNT 32 /* 128K */ + +#pragma pack(push, 1) + +/* Single-page buffer */ +struct hv_page_buffer { + u32 len; + u32 offset; + u64 pfn; +}; + +/* Multiple-page buffer */ +struct hv_multipage_buffer { + /* Length and Offset determines the # of pfns in the array */ + u32 len; + u32 offset; + u64 pfn_array[MAX_MULTIPAGE_BUFFER_COUNT]; +}; + +/* 0x18 includes the proprietary packet header */ +#define MAX_PAGE_BUFFER_PACKET (0x18 + \ + (sizeof(struct hv_page_buffer) * \ + MAX_PAGE_BUFFER_COUNT)) +#define MAX_MULTIPAGE_BUFFER_PACKET (0x18 + \ + sizeof(struct hv_multipage_buffer)) + + +#pragma pack(pop) + +struct hv_ring_buffer { + /* Offset in bytes from the start of ring data below */ + u32 write_index; + + /* Offset in bytes from the start of ring data below */ + u32 read_index; + + u32 interrupt_mask; + + /* Pad it to PAGE_SIZE so that data starts on page boundary */ + u8 reserved[4084]; + + /* NOTE: + * The interrupt_mask field is used only for channels but since our + * vmbus connection also uses this data structure and its data starts + * here, we commented out this field. + */ + + /* + * Ring data starts here + RingDataStartOffset + * !!! DO NOT place any fields below this !!! + */ + u8 buffer[0]; +} __packed; + +struct hv_ring_buffer_info { + struct hv_ring_buffer *ring_buffer; + u32 ring_size; /* Include the shared header */ + spinlock_t ring_lock; + + u32 ring_datasize; /* < ring_size */ + u32 ring_data_startoffset; +}; + +struct hv_ring_buffer_debug_info { + u32 current_interrupt_mask; + u32 current_read_index; + u32 current_write_index; + u32 bytes_avail_toread; + u32 bytes_avail_towrite; +}; + +/* + * We use the same version numbering for all Hyper-V modules. + * + * Definition of versioning is as follows; + * + * Major Number Changes for these scenarios; + * 1. When a new version of Windows Hyper-V + * is released. + * 2. A Major change has occurred in the + * Linux IC's. + * (For example the merge for the first time + * into the kernel) Every time the Major Number + * changes, the Revision number is reset to 0. + * Minor Number Changes when new functionality is added + * to the Linux IC's that is not a bug fix. + * + * 3.1 - Added completed hv_utils driver. Shutdown/Heartbeat/Timesync + */ +#define HV_DRV_VERSION "3.1" + + +/* + * A revision number of vmbus that is used for ensuring both ends on a + * partition are using compatible versions. + */ +#define VMBUS_REVISION_NUMBER 13 + +/* Make maximum size of pipe payload of 16K */ +#define MAX_PIPE_DATA_PAYLOAD (sizeof(u8) * 16384) + +/* Define PipeMode values. */ +#define VMBUS_PIPE_TYPE_BYTE 0x00000000 +#define VMBUS_PIPE_TYPE_MESSAGE 0x00000004 + +/* The size of the user defined data buffer for non-pipe offers. */ +#define MAX_USER_DEFINED_BYTES 120 + +/* The size of the user defined data buffer for pipe offers. */ +#define MAX_PIPE_USER_DEFINED_BYTES 116 + +/* + * At the center of the Channel Management library is the Channel Offer. This + * struct contains the fundamental information about an offer. + */ +struct vmbus_channel_offer { + struct hv_guid if_type; + struct hv_guid if_instance; + u64 int_latency; /* in 100ns units */ + u32 if_revision; + u32 server_ctx_size; /* in bytes */ + u16 chn_flags; + u16 mmio_megabytes; /* in bytes * 1024 * 1024 */ + + union { + /* Non-pipes: The user has MAX_USER_DEFINED_BYTES bytes. */ + struct { + unsigned char user_def[MAX_USER_DEFINED_BYTES]; + } std; + + /* + * Pipes: + * The following sructure is an integrated pipe protocol, which + * is implemented on top of standard user-defined data. Pipe + * clients have MAX_PIPE_USER_DEFINED_BYTES left for their own + * use. + */ + struct { + u32 pipe_mode; + unsigned char user_def[MAX_PIPE_USER_DEFINED_BYTES]; + } pipe; + } u; + u32 padding; +} __packed; + +/* Server Flags */ +#define VMBUS_CHANNEL_ENUMERATE_DEVICE_INTERFACE 1 +#define VMBUS_CHANNEL_SERVER_SUPPORTS_TRANSFER_PAGES 2 +#define VMBUS_CHANNEL_SERVER_SUPPORTS_GPADLS 4 +#define VMBUS_CHANNEL_NAMED_PIPE_MODE 0x10 +#define VMBUS_CHANNEL_LOOPBACK_OFFER 0x100 +#define VMBUS_CHANNEL_PARENT_OFFER 0x200 +#define VMBUS_CHANNEL_REQUEST_MONITORED_NOTIFICATION 0x400 + +struct vmpacket_descriptor { + u16 type; + u16 offset8; + u16 len8; + u16 flags; + u64 trans_id; +} __packed; + +struct vmpacket_header { + u32 prev_pkt_start_offset; + struct vmpacket_descriptor descriptor; +} __packed; + +struct vmtransfer_page_range { + u32 byte_count; + u32 byte_offset; +} __packed; + +struct vmtransfer_page_packet_header { + struct vmpacket_descriptor d; + u16 xfer_pageset_id; + bool sender_owns_set; + u8 reserved; + u32 range_cnt; + struct vmtransfer_page_range ranges[1]; +} __packed; + +struct vmgpadl_packet_header { + struct vmpacket_descriptor d; + u32 gpadl; + u32 reserved; +} __packed; + +struct vmadd_remove_transfer_page_set { + struct vmpacket_descriptor d; + u32 gpadl; + u16 xfer_pageset_id; + u16 reserved; +} __packed; + +/* + * This structure defines a range in guest physical space that can be made to + * look virtually contiguous. + */ +struct gpa_range { + u32 byte_count; + u32 byte_offset; + u64 pfn_array[0]; +}; + +/* + * This is the format for an Establish Gpadl packet, which contains a handle by + * which this GPADL will be known and a set of GPA ranges associated with it. + * This can be converted to a MDL by the guest OS. If there are multiple GPA + * ranges, then the resulting MDL will be "chained," representing multiple VA + * ranges. + */ +struct vmestablish_gpadl { + struct vmpacket_descriptor d; + u32 gpadl; + u32 range_cnt; + struct gpa_range range[1]; +} __packed; + +/* + * This is the format for a Teardown Gpadl packet, which indicates that the + * GPADL handle in the Establish Gpadl packet will never be referenced again. + */ +struct vmteardown_gpadl { + struct vmpacket_descriptor d; + u32 gpadl; + u32 reserved; /* for alignment to a 8-byte boundary */ +} __packed; + +/* + * This is the format for a GPA-Direct packet, which contains a set of GPA + * ranges, in addition to commands and/or data. + */ +struct vmdata_gpa_direct { + struct vmpacket_descriptor d; + u32 reserved; + u32 range_cnt; + struct gpa_range range[1]; +} __packed; + +/* This is the format for a Additional Data Packet. */ +struct vmadditional_data { + struct vmpacket_descriptor d; + u64 total_bytes; + u32 offset; + u32 byte_cnt; + unsigned char data[1]; +} __packed; + +union vmpacket_largest_possible_header { + struct vmpacket_descriptor simple_hdr; + struct vmtransfer_page_packet_header xfer_page_hdr; + struct vmgpadl_packet_header gpadl_hdr; + struct vmadd_remove_transfer_page_set add_rm_xfer_page_hdr; + struct vmestablish_gpadl establish_gpadl_hdr; + struct vmteardown_gpadl teardown_gpadl_hdr; + struct vmdata_gpa_direct data_gpa_direct_hdr; +}; + +#define VMPACKET_DATA_START_ADDRESS(__packet) \ + (void *)(((unsigned char *)__packet) + \ + ((struct vmpacket_descriptor)__packet)->offset8 * 8) + +#define VMPACKET_DATA_LENGTH(__packet) \ + ((((struct vmpacket_descriptor)__packet)->len8 - \ + ((struct vmpacket_descriptor)__packet)->offset8) * 8) + +#define VMPACKET_TRANSFER_MODE(__packet) \ + (((struct IMPACT)__packet)->type) + +enum vmbus_packet_type { + VM_PKT_INVALID = 0x0, + VM_PKT_SYNCH = 0x1, + VM_PKT_ADD_XFER_PAGESET = 0x2, + VM_PKT_RM_XFER_PAGESET = 0x3, + VM_PKT_ESTABLISH_GPADL = 0x4, + VM_PKT_TEARDOWN_GPADL = 0x5, + VM_PKT_DATA_INBAND = 0x6, + VM_PKT_DATA_USING_XFER_PAGES = 0x7, + VM_PKT_DATA_USING_GPADL = 0x8, + VM_PKT_DATA_USING_GPA_DIRECT = 0x9, + VM_PKT_CANCEL_REQUEST = 0xa, + VM_PKT_COMP = 0xb, + VM_PKT_DATA_USING_ADDITIONAL_PKT = 0xc, + VM_PKT_ADDITIONAL_DATA = 0xd +}; + +#define VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED 1 + + +/* Version 1 messages */ +enum vmbus_channel_message_type { + CHANNELMSG_INVALID = 0, + CHANNELMSG_OFFERCHANNEL = 1, + CHANNELMSG_RESCIND_CHANNELOFFER = 2, + CHANNELMSG_REQUESTOFFERS = 3, + CHANNELMSG_ALLOFFERS_DELIVERED = 4, + CHANNELMSG_OPENCHANNEL = 5, + CHANNELMSG_OPENCHANNEL_RESULT = 6, + CHANNELMSG_CLOSECHANNEL = 7, + CHANNELMSG_GPADL_HEADER = 8, + CHANNELMSG_GPADL_BODY = 9, + CHANNELMSG_GPADL_CREATED = 10, + CHANNELMSG_GPADL_TEARDOWN = 11, + CHANNELMSG_GPADL_TORNDOWN = 12, + CHANNELMSG_RELID_RELEASED = 13, + CHANNELMSG_INITIATE_CONTACT = 14, + CHANNELMSG_VERSION_RESPONSE = 15, + CHANNELMSG_UNLOAD = 16, +#ifdef VMBUS_FEATURE_PARENT_OR_PEER_MEMORY_MAPPED_INTO_A_CHILD + CHANNELMSG_VIEWRANGE_ADD = 17, + CHANNELMSG_VIEWRANGE_REMOVE = 18, +#endif + CHANNELMSG_COUNT +}; + +struct vmbus_channel_message_header { + enum vmbus_channel_message_type msgtype; + u32 padding; +} __packed; + +/* Query VMBus Version parameters */ +struct vmbus_channel_query_vmbus_version { + struct vmbus_channel_message_header header; + u32 version; +} __packed; + +/* VMBus Version Supported parameters */ +struct vmbus_channel_version_supported { + struct vmbus_channel_message_header header; + bool version_supported; +} __packed; + +/* Offer Channel parameters */ +struct vmbus_channel_offer_channel { + struct vmbus_channel_message_header header; + struct vmbus_channel_offer offer; + u32 child_relid; + u8 monitorid; + bool monitor_allocated; +} __packed; + +/* Rescind Offer parameters */ +struct vmbus_channel_rescind_offer { + struct vmbus_channel_message_header header; + u32 child_relid; +} __packed; + +/* + * Request Offer -- no parameters, SynIC message contains the partition ID + * Set Snoop -- no parameters, SynIC message contains the partition ID + * Clear Snoop -- no parameters, SynIC message contains the partition ID + * All Offers Delivered -- no parameters, SynIC message contains the partition + * ID + * Flush Client -- no parameters, SynIC message contains the partition ID + */ + +/* Open Channel parameters */ +struct vmbus_channel_open_channel { + struct vmbus_channel_message_header header; + + /* Identifies the specific VMBus channel that is being opened. */ + u32 child_relid; + + /* ID making a particular open request at a channel offer unique. */ + u32 openid; + + /* GPADL for the channel's ring buffer. */ + u32 ringbuffer_gpadlhandle; + + /* GPADL for the channel's server context save area. */ + u32 server_contextarea_gpadlhandle; + + /* + * The upstream ring buffer begins at offset zero in the memory + * described by RingBufferGpadlHandle. The downstream ring buffer + * follows it at this offset (in pages). + */ + u32 downstream_ringbuffer_pageoffset; + + /* User-specific data to be passed along to the server endpoint. */ + unsigned char userdata[MAX_USER_DEFINED_BYTES]; +} __packed; + +/* Open Channel Result parameters */ +struct vmbus_channel_open_result { + struct vmbus_channel_message_header header; + u32 child_relid; + u32 openid; + u32 status; +} __packed; + +/* Close channel parameters; */ +struct vmbus_channel_close_channel { + struct vmbus_channel_message_header header; + u32 child_relid; +} __packed; + +/* Channel Message GPADL */ +#define GPADL_TYPE_RING_BUFFER 1 +#define GPADL_TYPE_SERVER_SAVE_AREA 2 +#define GPADL_TYPE_TRANSACTION 8 + +/* + * The number of PFNs in a GPADL message is defined by the number of + * pages that would be spanned by ByteCount and ByteOffset. If the + * implied number of PFNs won't fit in this packet, there will be a + * follow-up packet that contains more. + */ +struct vmbus_channel_gpadl_header { + struct vmbus_channel_message_header header; + u32 child_relid; + u32 gpadl; + u16 range_buflen; + u16 rangecount; + struct gpa_range range[0]; +} __packed; + +/* This is the followup packet that contains more PFNs. */ +struct vmbus_channel_gpadl_body { + struct vmbus_channel_message_header header; + u32 msgnumber; + u32 gpadl; + u64 pfn[0]; +} __packed; + +struct vmbus_channel_gpadl_created { + struct vmbus_channel_message_header header; + u32 child_relid; + u32 gpadl; + u32 creation_status; +} __packed; + +struct vmbus_channel_gpadl_teardown { + struct vmbus_channel_message_header header; + u32 child_relid; + u32 gpadl; +} __packed; + +struct vmbus_channel_gpadl_torndown { + struct vmbus_channel_message_header header; + u32 gpadl; +} __packed; + +#ifdef VMBUS_FEATURE_PARENT_OR_PEER_MEMORY_MAPPED_INTO_A_CHILD +struct vmbus_channel_view_range_add { + struct vmbus_channel_message_header header; + PHYSICAL_ADDRESS viewrange_base; + u64 viewrange_length; + u32 child_relid; +} __packed; + +struct vmbus_channel_view_range_remove { + struct vmbus_channel_message_header header; + PHYSICAL_ADDRESS viewrange_base; + u32 child_relid; +} __packed; +#endif + +struct vmbus_channel_relid_released { + struct vmbus_channel_message_header header; + u32 child_relid; +} __packed; + +struct vmbus_channel_initiate_contact { + struct vmbus_channel_message_header header; + u32 vmbus_version_requested; + u32 padding2; + u64 interrupt_page; + u64 monitor_page1; + u64 monitor_page2; +} __packed; + +struct vmbus_channel_version_response { + struct vmbus_channel_message_header header; + bool version_supported; +} __packed; + +enum vmbus_channel_state { + CHANNEL_OFFER_STATE, + CHANNEL_OPENING_STATE, + CHANNEL_OPEN_STATE, +}; + +struct vmbus_channel_debug_info { + u32 relid; + enum vmbus_channel_state state; + struct hv_guid interfacetype; + struct hv_guid interface_instance; + u32 monitorid; + u32 servermonitor_pending; + u32 servermonitor_latency; + u32 servermonitor_connectionid; + u32 clientmonitor_pending; + u32 clientmonitor_latency; + u32 clientmonitor_connectionid; + + struct hv_ring_buffer_debug_info inbound; + struct hv_ring_buffer_debug_info outbound; +}; + +/* + * Represents each channel msg on the vmbus connection This is a + * variable-size data structure depending on the msg type itself + */ +struct vmbus_channel_msginfo { + /* Bookkeeping stuff */ + struct list_head msglistentry; + + /* So far, this is only used to handle gpadl body message */ + struct list_head submsglist; + + /* Synchronize the request/response if needed */ + struct completion waitevent; + union { + struct vmbus_channel_version_supported version_supported; + struct vmbus_channel_open_result open_result; + struct vmbus_channel_gpadl_torndown gpadl_torndown; + struct vmbus_channel_gpadl_created gpadl_created; + struct vmbus_channel_version_response version_response; + } response; + + u32 msgsize; + /* + * The channel message that goes out on the "wire". + * It will contain at minimum the VMBUS_CHANNEL_MESSAGE_HEADER header + */ + unsigned char msg[0]; +}; + +struct vmbus_close_msg { + struct vmbus_channel_msginfo info; + struct vmbus_channel_close_channel msg; +}; + +struct vmbus_channel { + struct list_head listentry; + + struct hv_device *device_obj; + + struct work_struct work; + + enum vmbus_channel_state state; + /* + * For util channels, stash the + * the service index for easy access. + */ + s8 util_index; + + struct vmbus_channel_offer_channel offermsg; + /* + * These are based on the OfferMsg.MonitorId. + * Save it here for easy access. + */ + u8 monitor_grp; + u8 monitor_bit; + + u32 ringbuffer_gpadlhandle; + + /* Allocated memory for ring buffer */ + void *ringbuffer_pages; + u32 ringbuffer_pagecount; + struct hv_ring_buffer_info outbound; /* send to parent */ + struct hv_ring_buffer_info inbound; /* receive from parent */ + spinlock_t inbound_lock; + struct workqueue_struct *controlwq; + + struct vmbus_close_msg close_msg; + + /* Channel callback are invoked in this workqueue context */ + /* HANDLE dataWorkQueue; */ + + void (*onchannel_callback)(void *context); + void *channel_callback_context; +}; + +void free_channel(struct vmbus_channel *channel); + +void vmbus_onmessage(void *context); + +int vmbus_request_offers(void); + +/* The format must be the same as struct vmdata_gpa_direct */ +struct vmbus_channel_packet_page_buffer { + u16 type; + u16 dataoffset8; + u16 length8; + u16 flags; + u64 transactionid; + u32 reserved; + u32 rangecount; + struct hv_page_buffer range[MAX_PAGE_BUFFER_COUNT]; +} __packed; + +/* The format must be the same as struct vmdata_gpa_direct */ +struct vmbus_channel_packet_multipage_buffer { + u16 type; + u16 dataoffset8; + u16 length8; + u16 flags; + u64 transactionid; + u32 reserved; + u32 rangecount; /* Always 1 in this case */ + struct hv_multipage_buffer range; +} __packed; + + +extern int vmbus_open(struct vmbus_channel *channel, + u32 send_ringbuffersize, + u32 recv_ringbuffersize, + void *userdata, + u32 userdatalen, + void(*onchannel_callback)(void *context), + void *context); + +extern void vmbus_close(struct vmbus_channel *channel); + +extern int vmbus_sendpacket(struct vmbus_channel *channel, + const void *buffer, + u32 bufferLen, + u64 requestid, + enum vmbus_packet_type type, + u32 flags); + +extern int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, + struct hv_page_buffer pagebuffers[], + u32 pagecount, + void *buffer, + u32 bufferlen, + u64 requestid); + +extern int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, + struct hv_multipage_buffer *mpb, + void *buffer, + u32 bufferlen, + u64 requestid); + +extern int vmbus_establish_gpadl(struct vmbus_channel *channel, + void *kbuffer, + u32 size, + u32 *gpadl_handle); + +extern int vmbus_teardown_gpadl(struct vmbus_channel *channel, + u32 gpadl_handle); + +extern int vmbus_recvpacket(struct vmbus_channel *channel, + void *buffer, + u32 bufferlen, + u32 *buffer_actual_len, + u64 *requestid); + +extern int vmbus_recvpacket_raw(struct vmbus_channel *channel, + void *buffer, + u32 bufferlen, + u32 *buffer_actual_len, + u64 *requestid); + + +extern void vmbus_get_debug_info(struct vmbus_channel *channel, + struct vmbus_channel_debug_info *debug); + +extern void vmbus_ontimer(unsigned long data); + + +#define LOWORD(dw) ((unsigned short)(dw)) +#define HIWORD(dw) ((unsigned short)(((unsigned int) (dw) >> 16) & 0xFFFF)) + + +#define VMBUS 0x0001 +#define STORVSC 0x0002 +#define NETVSC 0x0004 +#define INPUTVSC 0x0008 +#define BLKVSC 0x0010 +#define VMBUS_DRV 0x0100 +#define STORVSC_DRV 0x0200 +#define NETVSC_DRV 0x0400 +#define INPUTVSC_DRV 0x0800 +#define BLKVSC_DRV 0x1000 + +#define ALL_MODULES (VMBUS |\ + STORVSC |\ + NETVSC |\ + INPUTVSC |\ + BLKVSC |\ + VMBUS_DRV |\ + STORVSC_DRV |\ + NETVSC_DRV |\ + INPUTVSC_DRV|\ + BLKVSC_DRV) + +/* Logging Level */ +#define ERROR_LVL 3 +#define WARNING_LVL 4 +#define INFO_LVL 6 +#define DEBUG_LVL 7 +#define DEBUG_LVL_ENTEREXIT 8 +#define DEBUG_RING_LVL 9 + +extern unsigned int vmbus_loglevel; + +#define DPRINT(mod, lvl, fmt, args...) do {\ + if ((mod & (HIWORD(vmbus_loglevel))) && \ + (lvl <= LOWORD(vmbus_loglevel))) \ + printk(KERN_DEBUG #mod": %s() " fmt "\n", __func__, ## args);\ + } while (0) + +#define DPRINT_DBG(mod, fmt, args...) do {\ + if ((mod & (HIWORD(vmbus_loglevel))) && \ + (DEBUG_LVL <= LOWORD(vmbus_loglevel))) \ + printk(KERN_DEBUG #mod": %s() " fmt "\n", __func__, ## args);\ + } while (0) + +#define DPRINT_INFO(mod, fmt, args...) do {\ + if ((mod & (HIWORD(vmbus_loglevel))) && \ + (INFO_LVL <= LOWORD(vmbus_loglevel))) \ + printk(KERN_INFO #mod": " fmt "\n", ## args);\ + } while (0) + +#define DPRINT_WARN(mod, fmt, args...) do {\ + if ((mod & (HIWORD(vmbus_loglevel))) && \ + (WARNING_LVL <= LOWORD(vmbus_loglevel))) \ + printk(KERN_WARNING #mod": WARNING! " fmt "\n", ## args);\ + } while (0) + +#define DPRINT_ERR(mod, fmt, args...) do {\ + if ((mod & (HIWORD(vmbus_loglevel))) && \ + (ERROR_LVL <= LOWORD(vmbus_loglevel))) \ + printk(KERN_ERR #mod": %s() ERROR!! " fmt "\n", \ + __func__, ## args);\ + } while (0) + + + +struct hv_driver; +struct hv_device; + +struct hv_dev_port_info { + u32 int_mask; + u32 read_idx; + u32 write_idx; + u32 bytes_avail_toread; + u32 bytes_avail_towrite; +}; + +struct hv_device_info { + u32 chn_id; + u32 chn_state; + struct hv_guid chn_type; + struct hv_guid chn_instance; + + u32 monitor_id; + u32 server_monitor_pending; + u32 server_monitor_latency; + u32 server_monitor_conn_id; + u32 client_monitor_pending; + u32 client_monitor_latency; + u32 client_monitor_conn_id; + + struct hv_dev_port_info inbound; + struct hv_dev_port_info outbound; +}; + +/* Base driver object */ +struct hv_driver { + const char *name; + + /* the device type supported by this driver */ + struct hv_guid dev_type; + + struct device_driver driver; + + int (*probe)(struct hv_device *); + int (*remove)(struct hv_device *); + void (*shutdown)(struct hv_device *); + +}; + +/* Base device object */ +struct hv_device { + /* the device type id of this device */ + struct hv_guid dev_type; + + /* the device instance id of this device */ + struct hv_guid dev_instance; + + struct device device; + + struct vmbus_channel *channel; + + /* Device extension; */ + void *ext; +}; + + +static inline struct hv_device *device_to_hv_device(struct device *d) +{ + return container_of(d, struct hv_device, device); +} + +static inline struct hv_driver *drv_to_hv_drv(struct device_driver *d) +{ + return container_of(d, struct hv_driver, driver); +} + + +/* Vmbus interface */ +int vmbus_child_driver_register(struct device_driver *drv); +void vmbus_child_driver_unregister(struct device_driver *drv); + +/* + * Common header for Hyper-V ICs + */ + +#define ICMSGTYPE_NEGOTIATE 0 +#define ICMSGTYPE_HEARTBEAT 1 +#define ICMSGTYPE_KVPEXCHANGE 2 +#define ICMSGTYPE_SHUTDOWN 3 +#define ICMSGTYPE_TIMESYNC 4 +#define ICMSGTYPE_VSS 5 + +#define ICMSGHDRFLAG_TRANSACTION 1 +#define ICMSGHDRFLAG_REQUEST 2 +#define ICMSGHDRFLAG_RESPONSE 4 + +#define HV_S_OK 0x00000000 +#define HV_E_FAIL 0x80004005 +#define HV_ERROR_NOT_SUPPORTED 0x80070032 +#define HV_ERROR_MACHINE_LOCKED 0x800704F7 + +struct vmbuspipe_hdr { + u32 flags; + u32 msgsize; +} __packed; + +struct ic_version { + u16 major; + u16 minor; +} __packed; + +struct icmsg_hdr { + struct ic_version icverframe; + u16 icmsgtype; + struct ic_version icvermsg; + u16 icmsgsize; + u32 status; + u8 ictransaction_id; + u8 icflags; + u8 reserved[2]; +} __packed; + +struct icmsg_negotiate { + u16 icframe_vercnt; + u16 icmsg_vercnt; + u32 reserved; + struct ic_version icversion_data[1]; /* any size array */ +} __packed; + +struct shutdown_msg_data { + u32 reason_code; + u32 timeout_seconds; + u32 flags; + u8 display_message[2048]; +} __packed; + +struct heartbeat_msg_data { + u64 seq_num; + u32 reserved[8]; +} __packed; + +/* Time Sync IC defs */ +#define ICTIMESYNCFLAG_PROBE 0 +#define ICTIMESYNCFLAG_SYNC 1 +#define ICTIMESYNCFLAG_SAMPLE 2 + +#ifdef __x86_64__ +#define WLTIMEDELTA 116444736000000000L /* in 100ns unit */ +#else +#define WLTIMEDELTA 116444736000000000LL +#endif + +struct ictimesync_data { + u64 parenttime; + u64 childtime; + u64 roundtriptime; + u8 flags; +} __packed; + +/* Index for each IC struct in array hv_cb_utils[] */ +#define HV_SHUTDOWN_MSG 0 +#define HV_TIMESYNC_MSG 1 +#define HV_HEARTBEAT_MSG 2 +#define HV_KVP_MSG 3 + +struct hyperv_service_callback { + u8 msg_type; + char *log_msg; + unsigned char data[16]; + struct vmbus_channel *channel; + void (*callback) (void *context); +}; + +extern void prep_negotiate_resp(struct icmsg_hdr *, + struct icmsg_negotiate *, u8 *); +extern void chn_cb_negotiate(void *); +extern struct hyperv_service_callback hv_cb_utils[]; + +#endif /* _HYPERV_H */ diff --git a/drivers/staging/hv/rndis.h b/drivers/staging/hv/hyperv_net.h index 723e1f1..27f987b 100644 --- a/drivers/staging/hv/rndis.h +++ b/drivers/staging/hv/hyperv_net.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2009, Microsoft Corporation. + * Copyright (c) 2011, 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, @@ -18,11 +18,385 @@ * Authors: * Haiyang Zhang <haiyangz@microsoft.com> * Hank Janssen <hjanssen@microsoft.com> + * K. Y. Srinivasan <kys@microsoft.com> * */ -#ifndef _RNDIS_H_ -#define _RNDIS_H_ +#ifndef _HYPERV_NET_H +#define _HYPERV_NET_H + +#include <linux/list.h> +#include "hyperv.h" + +/* Fwd declaration */ +struct hv_netvsc_packet; + +/* Represent the xfer page packet which contains 1 or more netvsc packet */ +struct xferpage_packet { + struct list_head list_ent; + + /* # of netvsc packets this xfer packet contains */ + u32 count; +}; + +/* The number of pages which are enough to cover jumbo frame buffer. */ +#define NETVSC_PACKET_MAXPAGE 4 + +/* + * Represent netvsc packet which contains 1 RNDIS and 1 ethernet frame + * within the RNDIS + */ +struct hv_netvsc_packet { + /* Bookkeeping stuff */ + struct list_head list_ent; + + struct hv_device *device; + bool is_data_pkt; + + /* + * Valid only for receives when we break a xfer page packet + * into multiple netvsc packets + */ + struct xferpage_packet *xfer_page_pkt; + + union { + struct { + u64 recv_completion_tid; + void *recv_completion_ctx; + void (*recv_completion)(void *context); + } recv; + struct { + u64 send_completion_tid; + void *send_completion_ctx; + void (*send_completion)(void *context); + } send; + } completion; + + /* This points to the memory after page_buf */ + void *extension; + + u32 total_data_buflen; + /* Points to the send/receive buffer where the ethernet frame is */ + u32 page_buf_cnt; + struct hv_page_buffer page_buf[NETVSC_PACKET_MAXPAGE]; +}; + +struct netvsc_device_info { + unsigned char mac_adr[6]; + bool link_state; /* 0 - link up, 1 - link down */ + int ring_size; +}; + +/* Interface */ +int netvsc_device_add(struct hv_device *device, void *additional_info); +int netvsc_device_remove(struct hv_device *device); +int netvsc_send(struct hv_device *device, + struct hv_netvsc_packet *packet); +void netvsc_linkstatus_callback(struct hv_device *device_obj, + unsigned int status); +int netvsc_recv_callback(struct hv_device *device_obj, + struct hv_netvsc_packet *packet); +int netvsc_initialize(struct hv_driver *drv); +int rndis_filter_open(struct hv_device *dev); +int rndis_filter_close(struct hv_device *dev); +int rndis_filter_device_add(struct hv_device *dev, + void *additional_info); +void rndis_filter_device_remove(struct hv_device *dev); +int rndis_filter_receive(struct hv_device *dev, + struct hv_netvsc_packet *pkt); + + + +int rndis_filter_send(struct hv_device *dev, + struct hv_netvsc_packet *pkt); + +#define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF) + +#define NVSP_PROTOCOL_VERSION_1 2 +#define NVSP_MIN_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1 +#define NVSP_MAX_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1 + +enum { + NVSP_MSG_TYPE_NONE = 0, + + /* Init Messages */ + NVSP_MSG_TYPE_INIT = 1, + NVSP_MSG_TYPE_INIT_COMPLETE = 2, + + NVSP_VERSION_MSG_START = 100, + + /* Version 1 Messages */ + NVSP_MSG1_TYPE_SEND_NDIS_VER = NVSP_VERSION_MSG_START, + + NVSP_MSG1_TYPE_SEND_RECV_BUF, + NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE, + NVSP_MSG1_TYPE_REVOKE_RECV_BUF, + + NVSP_MSG1_TYPE_SEND_SEND_BUF, + NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE, + NVSP_MSG1_TYPE_REVOKE_SEND_BUF, + + NVSP_MSG1_TYPE_SEND_RNDIS_PKT, + NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE, + + /* + * This should be set to the number of messages for the version with + * the maximum number of messages. + */ + NVSP_NUM_MSG_PER_VERSION = 9, +}; + +enum { + NVSP_STAT_NONE = 0, + NVSP_STAT_SUCCESS, + NVSP_STAT_FAIL, + NVSP_STAT_PROTOCOL_TOO_NEW, + NVSP_STAT_PROTOCOL_TOO_OLD, + NVSP_STAT_INVALID_RNDIS_PKT, + NVSP_STAT_BUSY, + NVSP_STAT_MAX, +}; + +struct nvsp_message_header { + u32 msg_type; +}; + +/* Init Messages */ + +/* + * This message is used by the VSC to initialize the channel after the channels + * has been opened. This message should never include anything other then + * versioning (i.e. this message will be the same for ever). + */ +struct nvsp_message_init { + u32 min_protocol_ver; + u32 max_protocol_ver; +} __packed; + +/* + * This message is used by the VSP to complete the initialization of the + * channel. This message should never include anything other then versioning + * (i.e. this message will be the same for ever). + */ +struct nvsp_message_init_complete { + u32 negotiated_protocol_ver; + u32 max_mdl_chain_len; + u32 status; +} __packed; + +union nvsp_message_init_uber { + struct nvsp_message_init init; + struct nvsp_message_init_complete init_complete; +} __packed; + +/* Version 1 Messages */ + +/* + * This message is used by the VSC to send the NDIS version to the VSP. The VSP + * can use this information when handling OIDs sent by the VSC. + */ +struct nvsp_1_message_send_ndis_version { + u32 ndis_major_ver; + u32 ndis_minor_ver; +} __packed; + +/* + * This message is used by the VSC to send a receive buffer to the VSP. The VSP + * can then use the receive buffer to send data to the VSC. + */ +struct nvsp_1_message_send_receive_buffer { + u32 gpadl_handle; + u16 id; +} __packed; + +struct nvsp_1_receive_buffer_section { + u32 offset; + u32 sub_alloc_size; + u32 num_sub_allocs; + u32 end_offset; +} __packed; + +/* + * This message is used by the VSP to acknowledge a receive buffer send by the + * VSC. This message must be sent by the VSP before the VSP uses the receive + * buffer. + */ +struct nvsp_1_message_send_receive_buffer_complete { + u32 status; + u32 num_sections; + + /* + * The receive buffer is split into two parts, a large suballocation + * section and a small suballocation section. These sections are then + * suballocated by a certain size. + */ + + /* + * For example, the following break up of the receive buffer has 6 + * large suballocations and 10 small suballocations. + */ + + /* + * | Large Section | | Small Section | + * ------------------------------------------------------------ + * | | | | | | | | | | | | | | | | | | + * | | + * LargeOffset SmallOffset + */ + + struct nvsp_1_receive_buffer_section sections[1]; +} __packed; + +/* + * This message is sent by the VSC to revoke the receive buffer. After the VSP + * completes this transaction, the vsp should never use the receive buffer + * again. + */ +struct nvsp_1_message_revoke_receive_buffer { + u16 id; +}; + +/* + * This message is used by the VSC to send a send buffer to the VSP. The VSC + * can then use the send buffer to send data to the VSP. + */ +struct nvsp_1_message_send_send_buffer { + u32 gpadl_handle; + u16 id; +} __packed; + +/* + * This message is used by the VSP to acknowledge a send buffer sent by the + * VSC. This message must be sent by the VSP before the VSP uses the sent + * buffer. + */ +struct nvsp_1_message_send_send_buffer_complete { + u32 status; + + /* + * The VSC gets to choose the size of the send buffer and the VSP gets + * to choose the sections size of the buffer. This was done to enable + * dynamic reconfigurations when the cost of GPA-direct buffers + * decreases. + */ + u32 section_size; +} __packed; + +/* + * This message is sent by the VSC to revoke the send buffer. After the VSP + * completes this transaction, the vsp should never use the send buffer again. + */ +struct nvsp_1_message_revoke_send_buffer { + u16 id; +}; + +/* + * This message is used by both the VSP and the VSC to send a RNDIS message to + * the opposite channel endpoint. + */ +struct nvsp_1_message_send_rndis_packet { + /* + * This field is specified by RNIDS. They assume there's two different + * channels of communication. However, the Network VSP only has one. + * Therefore, the channel travels with the RNDIS packet. + */ + u32 channel_type; + + /* + * This field is used to send part or all of the data through a send + * buffer. This values specifies an index into the send buffer. If the + * index is 0xFFFFFFFF, then the send buffer is not being used and all + * of the data was sent through other VMBus mechanisms. + */ + u32 send_buf_section_index; + u32 send_buf_section_size; +} __packed; + +/* + * This message is used by both the VSP and the VSC to complete a RNDIS message + * to the opposite channel endpoint. At this point, the initiator of this + * message cannot use any resources associated with the original RNDIS packet. + */ +struct nvsp_1_message_send_rndis_packet_complete { + u32 status; +}; + +union nvsp_1_message_uber { + struct nvsp_1_message_send_ndis_version send_ndis_ver; + + struct nvsp_1_message_send_receive_buffer send_recv_buf; + struct nvsp_1_message_send_receive_buffer_complete + send_recv_buf_complete; + struct nvsp_1_message_revoke_receive_buffer revoke_recv_buf; + + struct nvsp_1_message_send_send_buffer send_send_buf; + struct nvsp_1_message_send_send_buffer_complete send_send_buf_complete; + struct nvsp_1_message_revoke_send_buffer revoke_send_buf; + + struct nvsp_1_message_send_rndis_packet send_rndis_pkt; + struct nvsp_1_message_send_rndis_packet_complete + send_rndis_pkt_complete; +} __packed; + +union nvsp_all_messages { + union nvsp_message_init_uber init_msg; + union nvsp_1_message_uber v1_msg; +} __packed; + +/* ALL Messages */ +struct nvsp_message { + struct nvsp_message_header hdr; + union nvsp_all_messages msg; +} __packed; + + + + +/* #define NVSC_MIN_PROTOCOL_VERSION 1 */ +/* #define NVSC_MAX_PROTOCOL_VERSION 1 */ + +#define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024) /* 1MB */ + +#define NETVSC_RECEIVE_BUFFER_ID 0xcafe + +#define NETVSC_RECEIVE_SG_COUNT 1 + +/* Preallocated receive packets */ +#define NETVSC_RECEIVE_PACKETLIST_COUNT 256 + +#define NETVSC_PACKET_SIZE 2048 + +/* Per netvsc channel-specific */ +struct netvsc_device { + struct hv_device *dev; + + atomic_t refcnt; + atomic_t num_outstanding_sends; + /* + * List of free preallocated hv_netvsc_packet to represent receive + * packet + */ + struct list_head recv_pkt_list; + spinlock_t recv_pkt_list_lock; + + /* Receive buffer allocated by us but manages by NetVSP */ + void *recv_buf; + u32 recv_buf_size; + u32 recv_buf_gpadl_handle; + u32 recv_section_cnt; + struct nvsp_1_receive_buffer_section *recv_section; + + /* Used for NetVSP initialization protocol */ + struct completion channel_init_wait; + struct nvsp_message channel_init_pkt; + + struct nvsp_message revoke_packet; + /* unsigned char HwMacAddr[HW_MACADDR_LEN]; */ + + /* Holds rndis device info */ + void *extension; +}; + /* Status codes */ @@ -288,24 +662,24 @@ #define RNDIS_DF_RAW_DATA 0x00000004 /* Remote NDIS medium types. */ -#define RNdisMedium802_3 0x00000000 -#define RNdisMedium802_5 0x00000001 -#define RNdisMediumFddi 0x00000002 -#define RNdisMediumWan 0x00000003 -#define RNdisMediumLocalTalk 0x00000004 -#define RNdisMediumArcnetRaw 0x00000006 -#define RNdisMediumArcnet878_2 0x00000007 -#define RNdisMediumAtm 0x00000008 -#define RNdisMediumWirelessWan 0x00000009 -#define RNdisMediumIrda 0x0000000a -#define RNdisMediumCoWan 0x0000000b +#define RNDIS_MEDIUM_802_3 0x00000000 +#define RNDIS_MEDIUM_802_5 0x00000001 +#define RNDIS_MEDIUM_FDDI 0x00000002 +#define RNDIS_MEDIUM_WAN 0x00000003 +#define RNDIS_MEDIUM_LOCAL_TALK 0x00000004 +#define RNDIS_MEDIUM_ARCNET_RAW 0x00000006 +#define RNDIS_MEDIUM_ARCNET_878_2 0x00000007 +#define RNDIS_MEDIUM_ATM 0x00000008 +#define RNDIS_MEDIUM_WIRELESS_WAN 0x00000009 +#define RNDIS_MEDIUM_IRDA 0x0000000a +#define RNDIS_MEDIUM_CO_WAN 0x0000000b /* Not a real medium, defined as an upper-bound */ -#define RNdisMediumMax 0x0000000d +#define RNDIS_MEDIUM_MAX 0x0000000d /* Remote NDIS medium connection states. */ -#define RNdisMediaStateConnected 0x00000000 -#define RNdisMediaStateDisconnected 0x00000001 +#define RNDIS_MEDIA_STATE_CONNECTED 0x00000000 +#define RNDIS_MEDIA_STATE_DISCONNECTED 0x00000001 /* Remote NDIS version numbers */ #define RNDIS_MAJOR_VERSION 0x00000001 @@ -314,106 +688,106 @@ /* NdisInitialize message */ struct rndis_initialize_request { - u32 RequestId; - u32 MajorVersion; - u32 MinorVersion; - u32 MaxTransferSize; + u32 req_id; + u32 major_ver; + u32 minor_ver; + u32 max_xfer_size; }; /* Response to NdisInitialize */ struct rndis_initialize_complete { - u32 RequestId; - u32 Status; - u32 MajorVersion; - u32 MinorVersion; - u32 DeviceFlags; - u32 Medium; - u32 MaxPacketsPerMessage; - u32 MaxTransferSize; - u32 PacketAlignmentFactor; - u32 AFListOffset; - u32 AFListSize; + u32 req_id; + u32 status; + u32 major_ver; + u32 minor_ver; + u32 dev_flags; + u32 medium; + u32 max_pkt_per_msg; + u32 max_xfer_size; + u32 pkt_alignment_factor; + u32 af_list_offset; + u32 af_list_size; }; /* Call manager devices only: Information about an address family */ /* supported by the device is appended to the response to NdisInitialize. */ struct rndis_co_address_family { - u32 AddressFamily; - u32 MajorVersion; - u32 MinorVersion; + u32 address_family; + u32 major_ver; + u32 minor_ver; }; /* NdisHalt message */ struct rndis_halt_request { - u32 RequestId; + u32 req_id; }; /* NdisQueryRequest message */ struct rndis_query_request { - u32 RequestId; - u32 Oid; - u32 InformationBufferLength; - u32 InformationBufferOffset; - u32 DeviceVcHandle; + u32 req_id; + u32 oid; + u32 info_buflen; + u32 info_buf_offset; + u32 dev_vc_handle; }; /* Response to NdisQueryRequest */ struct rndis_query_complete { - u32 RequestId; - u32 Status; - u32 InformationBufferLength; - u32 InformationBufferOffset; + u32 req_id; + u32 status; + u32 info_buflen; + u32 info_buf_offset; }; /* NdisSetRequest message */ struct rndis_set_request { - u32 RequestId; - u32 Oid; - u32 InformationBufferLength; - u32 InformationBufferOffset; - u32 DeviceVcHandle; + u32 req_id; + u32 oid; + u32 info_buflen; + u32 info_buf_offset; + u32 dev_vc_handle; }; /* Response to NdisSetRequest */ struct rndis_set_complete { - u32 RequestId; - u32 Status; + u32 req_id; + u32 status; }; /* NdisReset message */ struct rndis_reset_request { - u32 Reserved; + u32 reserved; }; /* Response to NdisReset */ struct rndis_reset_complete { - u32 Status; - u32 AddressingReset; + u32 status; + u32 addressing_reset; }; /* NdisMIndicateStatus message */ struct rndis_indicate_status { - u32 Status; - u32 StatusBufferLength; - u32 StatusBufferOffset; + u32 status; + u32 status_buflen; + u32 status_buf_offset; }; /* Diagnostic information passed as the status buffer in */ /* struct rndis_indicate_status messages signifying error conditions. */ struct rndis_diagnostic_info { - u32 DiagStatus; - u32 ErrorOffset; + u32 diag_status; + u32 error_offset; }; /* NdisKeepAlive message */ struct rndis_keepalive_request { - u32 RequestId; + u32 req_id; }; /* Response to NdisKeepAlive */ struct rndis_keepalive_complete { - u32 RequestId; - u32 Status; + u32 req_id; + u32 status; }; /* @@ -422,39 +796,39 @@ struct rndis_keepalive_complete { * to 0 for connectionless data, otherwise it contains the VC handle. */ struct rndis_packet { - u32 DataOffset; - u32 DataLength; - u32 OOBDataOffset; - u32 OOBDataLength; - u32 NumOOBDataElements; - u32 PerPacketInfoOffset; - u32 PerPacketInfoLength; - u32 VcHandle; - u32 Reserved; + u32 data_offset; + u32 data_len; + u32 oob_data_offset; + u32 oob_data_len; + u32 num_oob_data_elements; + u32 per_pkt_info_offset; + u32 per_pkt_info_len; + u32 vc_handle; + u32 reserved; }; /* Optional Out of Band data associated with a Data message. */ struct rndis_oobd { - u32 Size; - u32 Type; - u32 ClassInformationOffset; + u32 size; + u32 type; + u32 class_info_offset; }; /* Packet extension field contents associated with a Data message. */ struct rndis_per_packet_info { - u32 Size; - u32 Type; - u32 PerPacketInformationOffset; + u32 size; + u32 type; + u32 per_pkt_info_offset; }; /* Format of Information buffer passed in a SetRequest for the OID */ /* OID_GEN_RNDIS_CONFIG_PARAMETER. */ struct rndis_config_parameter_info { - u32 ParameterNameOffset; - u32 ParameterNameLength; - u32 ParameterType; - u32 ParameterValueOffset; - u32 ParameterValueLength; + u32 parameter_name_offset; + u32 parameter_name_length; + u32 parameter_type; + u32 parameter_value_offset; + u32 parameter_value_length; }; /* Values for ParameterType in struct rndis_config_parameter_info */ @@ -466,187 +840,218 @@ struct rndis_config_parameter_info { /* CoNdisMiniportCreateVc message */ struct rcondis_mp_create_vc { - u32 RequestId; - u32 NdisVcHandle; + u32 req_id; + u32 ndis_vc_handle; }; /* Response to CoNdisMiniportCreateVc */ struct rcondis_mp_create_vc_complete { - u32 RequestId; - u32 DeviceVcHandle; - u32 Status; + u32 req_id; + u32 dev_vc_handle; + u32 status; }; /* CoNdisMiniportDeleteVc message */ struct rcondis_mp_delete_vc { - u32 RequestId; - u32 DeviceVcHandle; + u32 req_id; + u32 dev_vc_handle; }; /* Response to CoNdisMiniportDeleteVc */ struct rcondis_mp_delete_vc_complete { - u32 RequestId; - u32 Status; + u32 req_id; + u32 status; }; /* CoNdisMiniportQueryRequest message */ struct rcondis_mp_query_request { - u32 RequestId; - u32 RequestType; - u32 Oid; - u32 DeviceVcHandle; - u32 InformationBufferLength; - u32 InformationBufferOffset; + u32 req_id; + u32 request_type; + u32 oid; + u32 dev_vc_handle; + u32 info_buflen; + u32 info_buf_offset; }; /* CoNdisMiniportSetRequest message */ struct rcondis_mp_set_request { - u32 RequestId; - u32 RequestType; - u32 Oid; - u32 DeviceVcHandle; - u32 InformationBufferLength; - u32 InformationBufferOffset; + u32 req_id; + u32 request_type; + u32 oid; + u32 dev_vc_handle; + u32 info_buflen; + u32 info_buf_offset; }; /* CoNdisIndicateStatus message */ struct rcondis_indicate_status { - u32 NdisVcHandle; - u32 Status; - u32 StatusBufferLength; - u32 StatusBufferOffset; + u32 ndis_vc_handle; + u32 status; + u32 status_buflen; + u32 status_buf_offset; }; /* CONDIS Call/VC parameters */ struct rcondis_specific_parameters { - u32 ParameterType; - u32 ParameterLength; - u32 ParameterOffset; + u32 parameter_type; + u32 parameter_length; + u32 parameter_lffset; }; struct rcondis_media_parameters { - u32 Flags; - u32 Reserved1; - u32 Reserved2; - struct rcondis_specific_parameters MediaSpecific; + u32 flags; + u32 reserved1; + u32 reserved2; + struct rcondis_specific_parameters media_specific; }; struct rndis_flowspec { - u32 TokenRate; - u32 TokenBucketSize; - u32 PeakBandwidth; - u32 Latency; - u32 DelayVariation; - u32 ServiceType; - u32 MaxSduSize; - u32 MinimumPolicedSize; + u32 token_rate; + u32 token_bucket_size; + u32 peak_bandwidth; + u32 latency; + u32 delay_variation; + u32 service_type; + u32 max_sdu_size; + u32 minimum_policed_size; }; struct rcondis_call_manager_parameters { - struct rndis_flowspec Transmit; - struct rndis_flowspec Receive; - struct rcondis_specific_parameters CallMgrSpecific; + struct rndis_flowspec transmit; + struct rndis_flowspec receive; + struct rcondis_specific_parameters call_mgr_specific; }; /* CoNdisMiniportActivateVc message */ struct rcondis_mp_activate_vc_request { - u32 RequestId; - u32 Flags; - u32 DeviceVcHandle; - u32 MediaParamsOffset; - u32 MediaParamsLength; - u32 CallMgrParamsOffset; - u32 CallMgrParamsLength; + u32 req_id; + u32 flags; + u32 dev_vc_handle; + u32 media_params_offset; + u32 media_params_length; + u32 call_mgr_params_offset; + u32 call_mgr_params_length; }; /* Response to CoNdisMiniportActivateVc */ struct rcondis_mp_activate_vc_complete { - u32 RequestId; - u32 Status; + u32 req_id; + u32 status; }; /* CoNdisMiniportDeactivateVc message */ struct rcondis_mp_deactivate_vc_request { - u32 RequestId; - u32 Flags; - u32 DeviceVcHandle; + u32 req_id; + u32 flags; + u32 dev_vc_handle; }; /* Response to CoNdisMiniportDeactivateVc */ struct rcondis_mp_deactivate_vc_complete { - u32 RequestId; - u32 Status; + u32 req_id; + u32 status; }; /* union with all of the RNDIS messages */ union rndis_message_container { - struct rndis_packet Packet; - struct rndis_initialize_request InitializeRequest; - struct rndis_halt_request HaltRequest; - struct rndis_query_request QueryRequest; - struct rndis_set_request SetRequest; - struct rndis_reset_request ResetRequest; - struct rndis_keepalive_request KeepaliveRequest; - struct rndis_indicate_status IndicateStatus; - struct rndis_initialize_complete InitializeComplete; - struct rndis_query_complete QueryComplete; - struct rndis_set_complete SetComplete; - struct rndis_reset_complete ResetComplete; - struct rndis_keepalive_complete KeepaliveComplete; - struct rcondis_mp_create_vc CoMiniportCreateVc; - struct rcondis_mp_delete_vc CoMiniportDeleteVc; - struct rcondis_indicate_status CoIndicateStatus; - struct rcondis_mp_activate_vc_request CoMiniportActivateVc; - struct rcondis_mp_deactivate_vc_request CoMiniportDeactivateVc; - struct rcondis_mp_create_vc_complete CoMiniportCreateVcComplete; - struct rcondis_mp_delete_vc_complete CoMiniportDeleteVcComplete; - struct rcondis_mp_activate_vc_complete CoMiniportActivateVcComplete; - struct rcondis_mp_deactivate_vc_complete CoMiniportDeactivateVcComplete; + struct rndis_packet pkt; + struct rndis_initialize_request init_req; + struct rndis_halt_request halt_req; + struct rndis_query_request query_req; + struct rndis_set_request set_req; + struct rndis_reset_request reset_req; + struct rndis_keepalive_request keep_alive_req; + struct rndis_indicate_status indicate_status; + struct rndis_initialize_complete init_complete; + struct rndis_query_complete query_complete; + struct rndis_set_complete set_complete; + struct rndis_reset_complete reset_complete; + struct rndis_keepalive_complete keep_alive_complete; + struct rcondis_mp_create_vc co_miniport_create_vc; + struct rcondis_mp_delete_vc co_miniport_delete_vc; + struct rcondis_indicate_status co_indicate_status; + struct rcondis_mp_activate_vc_request co_miniport_activate_vc; + struct rcondis_mp_deactivate_vc_request co_miniport_deactivate_vc; + struct rcondis_mp_create_vc_complete co_miniport_create_vc_complete; + struct rcondis_mp_delete_vc_complete co_miniport_delete_vc_complete; + struct rcondis_mp_activate_vc_complete co_miniport_activate_vc_complete; + struct rcondis_mp_deactivate_vc_complete + co_miniport_deactivate_vc_complete; }; /* Remote NDIS message format */ struct rndis_message { - u32 NdisMessageType; + u32 ndis_msg_type; /* Total length of this message, from the beginning */ /* of the sruct rndis_message, in bytes. */ - u32 MessageLength; + u32 msg_len; /* Actual message */ - union rndis_message_container Message; + union rndis_message_container msg; +}; + + +struct rndis_filter_packet { + void *completion_ctx; + void (*completion)(void *context); + struct rndis_message msg; }; /* Handy macros */ /* get the size of an RNDIS message. Pass in the message type, */ /* struct rndis_set_request, struct rndis_packet for example */ -#define RNDIS_MESSAGE_SIZE(Message) \ - (sizeof(Message) + (sizeof(struct rndis_message) - \ +#define RNDIS_MESSAGE_SIZE(msg) \ + (sizeof(msg) + (sizeof(struct rndis_message) - \ sizeof(union rndis_message_container))) /* get pointer to info buffer with message pointer */ -#define MESSAGE_TO_INFO_BUFFER(Message) \ - (((unsigned char *)(Message)) + Message->InformationBufferOffset) +#define MESSAGE_TO_INFO_BUFFER(msg) \ + (((unsigned char *)(msg)) + msg->info_buf_offset) /* get pointer to status buffer with message pointer */ -#define MESSAGE_TO_STATUS_BUFFER(Message) \ - (((unsigned char *)(Message)) + Message->StatusBufferOffset) +#define MESSAGE_TO_STATUS_BUFFER(msg) \ + (((unsigned char *)(msg)) + msg->status_buf_offset) /* get pointer to OOBD buffer with message pointer */ -#define MESSAGE_TO_OOBD_BUFFER(Message) \ - (((unsigned char *)(Message)) + Message->OOBDataOffset) +#define MESSAGE_TO_OOBD_BUFFER(msg) \ + (((unsigned char *)(msg)) + msg->oob_data_offset) /* get pointer to data buffer with message pointer */ -#define MESSAGE_TO_DATA_BUFFER(Message) \ - (((unsigned char *)(Message)) + Message->PerPacketInfoOffset) +#define MESSAGE_TO_DATA_BUFFER(msg) \ + (((unsigned char *)(msg)) + msg->per_pkt_info_offset) /* get pointer to contained message from NDIS_MESSAGE pointer */ -#define RNDIS_MESSAGE_PTR_TO_MESSAGE_PTR(RndisMessage) \ - ((void *) &RndisMessage->Message) +#define RNDIS_MESSAGE_PTR_TO_MESSAGE_PTR(rndis_msg) \ + ((void *) &rndis_msg->msg) /* get pointer to contained message from NDIS_MESSAGE pointer */ -#define RNDIS_MESSAGE_RAW_PTR_TO_MESSAGE_PTR(RndisMessage) \ - ((void *) RndisMessage) +#define RNDIS_MESSAGE_RAW_PTR_TO_MESSAGE_PTR(rndis_msg) \ + ((void *) rndis_msg) + + +#define __struct_bcount(x) + + + +#define RNDIS_HEADER_SIZE (sizeof(struct rndis_message) - \ + sizeof(union rndis_message_container)) + +#define NDIS_PACKET_TYPE_DIRECTED 0x00000001 +#define NDIS_PACKET_TYPE_MULTICAST 0x00000002 +#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 +#define NDIS_PACKET_TYPE_BROADCAST 0x00000008 +#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 +#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 +#define NDIS_PACKET_TYPE_SMT 0x00000040 +#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 +#define NDIS_PACKET_TYPE_GROUP 0x00000100 +#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200 +#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 +#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 + + -#endif /* _RNDIS_H_ */ +#endif /* _HYPERV_NET_H */ diff --git a/drivers/staging/hv/hyperv_storage.h b/drivers/staging/hv/hyperv_storage.h new file mode 100644 index 0000000..a01f9a0 --- a/dev/null +++ b/drivers/staging/hv/hyperv_storage.h @@ -0,0 +1,334 @@ +/* + * + * Copyright (c) 2011, 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> + * K. Y. Srinivasan <kys@microsoft.com> + * + */ + +#ifndef _HYPERV_STORAGE_H +#define _HYPERV_STORAGE_H + + +/* vstorage.w revision number. This is used in the case of a version match, */ +/* to alert the user that structure sizes may be mismatched even though the */ +/* protocol versions match. */ + + +#define REVISION_STRING(REVISION_) #REVISION_ +#define FILL_VMSTOR_REVISION(RESULT_LVALUE_) \ + do { \ + char *revision_string \ + = REVISION_STRING($Rev : 6 $) + 6; \ + RESULT_LVALUE_ = 0; \ + while (*revision_string >= '0' \ + && *revision_string <= '9') { \ + RESULT_LVALUE_ *= 10; \ + RESULT_LVALUE_ += *revision_string - '0'; \ + revision_string++; \ + } \ + } while (0) + +/* Major/minor macros. Minor version is in LSB, meaning that earlier flat */ +/* version numbers will be interpreted as "0.x" (i.e., 1 becomes 0.1). */ +#define VMSTOR_PROTOCOL_MAJOR(VERSION_) (((VERSION_) >> 8) & 0xff) +#define VMSTOR_PROTOCOL_MINOR(VERSION_) (((VERSION_)) & 0xff) +#define VMSTOR_PROTOCOL_VERSION(MAJOR_, MINOR_) ((((MAJOR_) & 0xff) << 8) | \ + (((MINOR_) & 0xff))) +#define VMSTOR_INVALID_PROTOCOL_VERSION (-1) + +/* Version history: */ +/* V1 Beta 0.1 */ +/* V1 RC < 2008/1/31 1.0 */ +/* V1 RC > 2008/1/31 2.0 */ +#define VMSTOR_PROTOCOL_VERSION_CURRENT VMSTOR_PROTOCOL_VERSION(2, 0) + + + + +/* This will get replaced with the max transfer length that is possible on */ +/* the host adapter. */ +/* The max transfer length will be published when we offer a vmbus channel. */ +#define MAX_TRANSFER_LENGTH 0x40000 +#define DEFAULT_PACKET_SIZE (sizeof(struct vmdata_gpa_direct) + \ + sizeof(struct vstor_packet) + \ + sizesizeof(u64) * (MAX_TRANSFER_LENGTH / PAGE_SIZE))) + + +/* Packet structure describing virtual storage requests. */ +enum vstor_packet_operation { + VSTOR_OPERATION_COMPLETE_IO = 1, + VSTOR_OPERATION_REMOVE_DEVICE = 2, + VSTOR_OPERATION_EXECUTE_SRB = 3, + VSTOR_OPERATION_RESET_LUN = 4, + VSTOR_OPERATION_RESET_ADAPTER = 5, + VSTOR_OPERATION_RESET_BUS = 6, + VSTOR_OPERATION_BEGIN_INITIALIZATION = 7, + VSTOR_OPERATION_END_INITIALIZATION = 8, + VSTOR_OPERATION_QUERY_PROTOCOL_VERSION = 9, + VSTOR_OPERATION_QUERY_PROPERTIES = 10, + VSTOR_OPERATION_MAXIMUM = 10 +}; + +/* + * Platform neutral description of a scsi request - + * this remains the same across the write regardless of 32/64 bit + * note: it's patterned off the SCSI_PASS_THROUGH structure + */ +#define CDB16GENERIC_LENGTH 0x10 + +#ifndef SENSE_BUFFER_SIZE +#define SENSE_BUFFER_SIZE 0x12 +#endif + +#define MAX_DATA_BUF_LEN_WITH_PADDING 0x14 + +struct vmscsi_request { + unsigned short length; + unsigned char srb_status; + unsigned char scsi_status; + + unsigned char port_number; + unsigned char path_id; + unsigned char target_id; + unsigned char lun; + + unsigned char cdb_length; + unsigned char sense_info_length; + unsigned char data_in; + unsigned char reserved; + + unsigned int data_transfer_length; + + union { + unsigned char cdb[CDB16GENERIC_LENGTH]; + unsigned char sense_data[SENSE_BUFFER_SIZE]; + unsigned char reserved_array[MAX_DATA_BUF_LEN_WITH_PADDING]; + }; +} __attribute((packed)); + + +/* + * This structure is sent during the intialization phase to get the different + * properties of the channel. + */ +struct vmstorage_channel_properties { + unsigned short protocol_version; + unsigned char path_id; + unsigned char target_id; + + /* Note: port number is only really known on the client side */ + unsigned int port_number; + unsigned int flags; + unsigned int max_transfer_bytes; + + /* This id is unique for each channel and will correspond with */ + /* vendor specific data in the inquirydata */ + unsigned long long unique_id; +} __packed; + +/* This structure is sent during the storage protocol negotiations. */ +struct vmstorage_protocol_version { + /* Major (MSW) and minor (LSW) version numbers. */ + unsigned short major_minor; + + /* + * Revision number is auto-incremented whenever this file is changed + * (See FILL_VMSTOR_REVISION macro above). Mismatch does not + * definitely indicate incompatibility--but it does indicate mismatched + * builds. + */ + unsigned short revision; +} __packed; + +/* Channel Property Flags */ +#define STORAGE_CHANNEL_REMOVABLE_FLAG 0x1 +#define STORAGE_CHANNEL_EMULATED_IDE_FLAG 0x2 + +struct vstor_packet { + /* Requested operation type */ + enum vstor_packet_operation operation; + + /* Flags - see below for values */ + unsigned int flags; + + /* Status of the request returned from the server side. */ + unsigned int status; + + /* Data payload area */ + union { + /* + * Structure used to forward SCSI commands from the + * client to the server. + */ + struct vmscsi_request vm_srb; + + /* Structure used to query channel properties. */ + struct vmstorage_channel_properties storage_channel_properties; + + /* Used during version negotiations. */ + struct vmstorage_protocol_version version; + }; +} __packed; + +/* Packet flags */ +/* + * This flag indicates that the server should send back a completion for this + * packet. + */ +#define REQUEST_COMPLETION_FLAG 0x1 + +/* This is the set of flags that the vsc can set in any packets it sends */ +#define VSC_LEGAL_FLAGS (REQUEST_COMPLETION_FLAG) + + +#include <linux/kernel.h> +#include <linux/wait.h> +#include "hyperv_storage.h" +#include "hyperv.h" + +/* Defines */ +#define STORVSC_RING_BUFFER_SIZE (20*PAGE_SIZE) +#define BLKVSC_RING_BUFFER_SIZE (20*PAGE_SIZE) + +#define STORVSC_MAX_IO_REQUESTS 128 + +/* + * In Hyper-V, each port/path/target maps to 1 scsi host adapter. In + * reality, the path/target is not used (ie always set to 0) so our + * scsi host adapter essentially has 1 bus with 1 target that contains + * up to 256 luns. + */ +#define STORVSC_MAX_LUNS_PER_TARGET 64 +#define STORVSC_MAX_TARGETS 1 +#define STORVSC_MAX_CHANNELS 1 + +struct hv_storvsc_request; + +/* Matches Windows-end */ +enum storvsc_request_type { + WRITE_TYPE, + READ_TYPE, + UNKNOWN_TYPE, +}; + + +struct hv_storvsc_request { + struct hv_storvsc_request *request; + struct hv_device *device; + + /* Synchronize the request/response if needed */ + struct completion wait_event; + + unsigned char *sense_buffer; + void *context; + void (*on_io_completion)(struct hv_storvsc_request *request); + struct hv_multipage_buffer data_buffer; + + struct vstor_packet vstor_packet; +}; + + +struct storvsc_device_info { + u32 ring_buffer_size; + unsigned int port_number; + unsigned char path_id; + unsigned char target_id; +}; + +struct storvsc_major_info { + int major; + int index; + bool do_register; + char *devname; + char *diskname; +}; + +/* A storvsc device is a device object that contains a vmbus channel */ +struct storvsc_device { + struct hv_device *device; + + /* 0 indicates the device is being destroyed */ + atomic_t ref_count; + + bool drain_notify; + atomic_t num_outstanding_req; + + wait_queue_head_t waiting_to_drain; + + /* + * Each unique Port/Path/Target represents 1 channel ie scsi + * controller. In reality, the pathid, targetid is always 0 + * and the port is set by us + */ + unsigned int port_number; + unsigned char path_id; + unsigned char target_id; + + /* Used for vsc/vsp channel reset process */ + struct hv_storvsc_request init_request; + struct hv_storvsc_request reset_request; +}; + + +/* Get the stordevice object iff exists and its refcount > 1 */ +static inline struct storvsc_device *get_stor_device(struct hv_device *device) +{ + struct storvsc_device *stor_device; + + stor_device = (struct storvsc_device *)device->ext; + if (stor_device && atomic_read(&stor_device->ref_count) > 1) + atomic_inc(&stor_device->ref_count); + else + stor_device = NULL; + + return stor_device; +} + + +static inline void put_stor_device(struct hv_device *device) +{ + struct storvsc_device *stor_device; + + stor_device = (struct storvsc_device *)device->ext; + + atomic_dec(&stor_device->ref_count); +} + +static inline void storvsc_wait_to_drain(struct storvsc_device *dev) +{ + dev->drain_notify = true; + wait_event(dev->waiting_to_drain, + atomic_read(&dev->num_outstanding_req) == 0); + dev->drain_notify = false; +} + +/* Interface */ + +int storvsc_dev_add(struct hv_device *device, + void *additional_info); +int storvsc_dev_remove(struct hv_device *device); + +int storvsc_do_io(struct hv_device *device, + struct hv_storvsc_request *request); + +int storvsc_get_major_info(struct storvsc_device_info *device_info, + struct storvsc_major_info *major_info); + +#endif /* _HYPERV_STORAGE_H */ diff --git a/drivers/staging/hv/hyperv_vmbus.h b/drivers/staging/hv/hyperv_vmbus.h new file mode 100644 index 0000000..349ad80 --- a/dev/null +++ b/drivers/staging/hv/hyperv_vmbus.h @@ -0,0 +1,629 @@ +/* + * + * Copyright (c) 2011, 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> + * K. Y. Srinivasan <kys@microsoft.com> + * + */ + +#ifndef _HYPERV_VMBUS_H +#define _HYPERV_VMBUS_H + +#include <linux/list.h> +#include <asm/sync_bitops.h> +#include <linux/atomic.h> + +#include "hyperv.h" + +/* + * The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent + * is set by CPUID(HVCPUID_VERSION_FEATURES). + */ +enum hv_cpuid_function { + HVCPUID_VERSION_FEATURES = 0x00000001, + HVCPUID_VENDOR_MAXFUNCTION = 0x40000000, + HVCPUID_INTERFACE = 0x40000001, + + /* + * The remaining functions depend on the value of + * HVCPUID_INTERFACE + */ + HVCPUID_VERSION = 0x40000002, + HVCPUID_FEATURES = 0x40000003, + HVCPUID_ENLIGHTENMENT_INFO = 0x40000004, + HVCPUID_IMPLEMENTATION_LIMITS = 0x40000005, +}; + +/* Define version of the synthetic interrupt controller. */ +#define HV_SYNIC_VERSION (1) + +/* Define the expected SynIC version. */ +#define HV_SYNIC_VERSION_1 (0x1) + +/* Define synthetic interrupt controller message constants. */ +#define HV_MESSAGE_SIZE (256) +#define HV_MESSAGE_PAYLOAD_BYTE_COUNT (240) +#define HV_MESSAGE_PAYLOAD_QWORD_COUNT (30) +#define HV_ANY_VP (0xFFFFFFFF) + +/* Define synthetic interrupt controller flag constants. */ +#define HV_EVENT_FLAGS_COUNT (256 * 8) +#define HV_EVENT_FLAGS_BYTE_COUNT (256) +#define HV_EVENT_FLAGS_DWORD_COUNT (256 / sizeof(u32)) + +/* Define hypervisor message types. */ +enum hv_message_type { + HVMSG_NONE = 0x00000000, + + /* Memory access messages. */ + HVMSG_UNMAPPED_GPA = 0x80000000, + HVMSG_GPA_INTERCEPT = 0x80000001, + + /* Timer notification messages. */ + HVMSG_TIMER_EXPIRED = 0x80000010, + + /* Error messages. */ + HVMSG_INVALID_VP_REGISTER_VALUE = 0x80000020, + HVMSG_UNRECOVERABLE_EXCEPTION = 0x80000021, + HVMSG_UNSUPPORTED_FEATURE = 0x80000022, + + /* Trace buffer complete messages. */ + HVMSG_EVENTLOG_BUFFERCOMPLETE = 0x80000040, + + /* Platform-specific processor intercept messages. */ + HVMSG_X64_IOPORT_INTERCEPT = 0x80010000, + HVMSG_X64_MSR_INTERCEPT = 0x80010001, + HVMSG_X64_CPUID_INTERCEPT = 0x80010002, + HVMSG_X64_EXCEPTION_INTERCEPT = 0x80010003, + HVMSG_X64_APIC_EOI = 0x80010004, + HVMSG_X64_LEGACY_FP_ERROR = 0x80010005 +}; + +/* Define the number of synthetic interrupt sources. */ +#define HV_SYNIC_SINT_COUNT (16) +#define HV_SYNIC_STIMER_COUNT (4) + +/* Define invalid partition identifier. */ +#define HV_PARTITION_ID_INVALID ((u64)0x0) + +/* Define connection identifier type. */ +union hv_connection_id { + u32 asu32; + struct { + u32 id:24; + u32 reserved:8; + } u; +}; + +/* Define port identifier type. */ +union hv_port_id { + u32 asu32; + struct { + u32 id:24; + u32 reserved:8; + } u ; +}; + +/* Define port type. */ +enum hv_port_type { + HVPORT_MSG = 1, + HVPORT_EVENT = 2, + HVPORT_MONITOR = 3 +}; + +/* Define port information structure. */ +struct hv_port_info { + enum hv_port_type port_type; + u32 padding; + union { + struct { + u32 target_sint; + u32 target_vp; + u64 rsvdz; + } message_port_info; + struct { + u32 target_sint; + u32 target_vp; + u16 base_flag_bumber; + u16 flag_count; + u32 rsvdz; + } event_port_info; + struct { + u64 monitor_address; + u64 rsvdz; + } monitor_port_info; + }; +}; + +struct hv_connection_info { + enum hv_port_type port_type; + u32 padding; + union { + struct { + u64 rsvdz; + } message_connection_info; + struct { + u64 rsvdz; + } event_connection_info; + struct { + u64 monitor_address; + } monitor_connection_info; + }; +}; + +/* Define synthetic interrupt controller message flags. */ +union hv_message_flags { + u8 asu8; + struct { + u8 msg_pending:1; + u8 reserved:7; + }; +}; + +/* Define synthetic interrupt controller message header. */ +struct hv_message_header { + enum hv_message_type message_type; + u8 payload_size; + union hv_message_flags message_flags; + u8 reserved[2]; + union { + u64 sender; + union hv_port_id port; + }; +}; + +/* Define timer message payload structure. */ +struct hv_timer_message_payload { + u32 timer_index; + u32 reserved; + u64 expiration_time; /* When the timer expired */ + u64 delivery_time; /* When the message was delivered */ +}; + +/* Define synthetic interrupt controller message format. */ +struct hv_message { + struct hv_message_header header; + union { + u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; + } u ; +}; + +/* Define the number of message buffers associated with each port. */ +#define HV_PORT_MESSAGE_BUFFER_COUNT (16) + +/* Define the synthetic interrupt message page layout. */ +struct hv_message_page { + struct hv_message sint_message[HV_SYNIC_SINT_COUNT]; +}; + +/* Define the synthetic interrupt controller event flags format. */ +union hv_synic_event_flags { + u8 flags8[HV_EVENT_FLAGS_BYTE_COUNT]; + u32 flags32[HV_EVENT_FLAGS_DWORD_COUNT]; +}; + +/* Define the synthetic interrupt flags page layout. */ +struct hv_synic_event_flags_page { + union hv_synic_event_flags sintevent_flags[HV_SYNIC_SINT_COUNT]; +}; + +/* Define SynIC control register. */ +union hv_synic_scontrol { + u64 as_uint64; + struct { + u64 enable:1; + u64 reserved:63; + }; +}; + +/* Define synthetic interrupt source. */ +union hv_synic_sint { + u64 as_uint64; + struct { + u64 vector:8; + u64 reserved1:8; + u64 masked:1; + u64 auto_eoi:1; + u64 reserved2:46; + }; +}; + +/* Define the format of the SIMP register */ +union hv_synic_simp { + u64 as_uint64; + struct { + u64 simp_enabled:1; + u64 preserved:11; + u64 base_simp_gpa:52; + }; +}; + +/* Define the format of the SIEFP register */ +union hv_synic_siefp { + u64 as_uint64; + struct { + u64 siefp_enabled:1; + u64 preserved:11; + u64 base_siefp_gpa:52; + }; +}; + +/* Definitions for the monitored notification facility */ +union hv_monitor_trigger_group { + u64 as_uint64; + struct { + u32 pending; + u32 armed; + }; +}; + +struct hv_monitor_parameter { + union hv_connection_id connectionid; + u16 flagnumber; + u16 rsvdz; +}; + +union hv_monitor_trigger_state { + u32 asu32; + + struct { + u32 group_enable:4; + u32 rsvdz:28; + }; +}; + +/* struct hv_monitor_page Layout */ +/* ------------------------------------------------------ */ +/* | 0 | TriggerState (4 bytes) | Rsvd1 (4 bytes) | */ +/* | 8 | TriggerGroup[0] | */ +/* | 10 | TriggerGroup[1] | */ +/* | 18 | TriggerGroup[2] | */ +/* | 20 | TriggerGroup[3] | */ +/* | 28 | Rsvd2[0] | */ +/* | 30 | Rsvd2[1] | */ +/* | 38 | Rsvd2[2] | */ +/* | 40 | NextCheckTime[0][0] | NextCheckTime[0][1] | */ +/* | ... | */ +/* | 240 | Latency[0][0..3] | */ +/* | 340 | Rsvz3[0] | */ +/* | 440 | Parameter[0][0] | */ +/* | 448 | Parameter[0][1] | */ +/* | ... | */ +/* | 840 | Rsvd4[0] | */ +/* ------------------------------------------------------ */ +struct hv_monitor_page { + union hv_monitor_trigger_state trigger_state; + u32 rsvdz1; + + union hv_monitor_trigger_group trigger_group[4]; + u64 rsvdz2[3]; + + s32 next_checktime[4][32]; + + u16 latency[4][32]; + u64 rsvdz3[32]; + + struct hv_monitor_parameter parameter[4][32]; + + u8 rsvdz4[1984]; +}; + +/* Declare the various hypercall operations. */ +enum hv_call_code { + HVCALL_POST_MESSAGE = 0x005c, + HVCALL_SIGNAL_EVENT = 0x005d, +}; + +/* Definition of the hv_post_message hypercall input structure. */ +struct hv_input_post_message { + union hv_connection_id connectionid; + u32 reserved; + enum hv_message_type message_type; + u32 payload_size; + u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; +}; + +/* Definition of the hv_signal_event hypercall input structure. */ +struct hv_input_signal_event { + union hv_connection_id connectionid; + u16 flag_number; + u16 rsvdz; +}; + +/* + * Versioning definitions used for guests reporting themselves to the + * hypervisor, and visa versa. + */ + +/* Version info reported by guest OS's */ +enum hv_guest_os_vendor { + HVGUESTOS_VENDOR_MICROSOFT = 0x0001 +}; + +enum hv_guest_os_microsoft_ids { + HVGUESTOS_MICROSOFT_UNDEFINED = 0x00, + HVGUESTOS_MICROSOFT_MSDOS = 0x01, + HVGUESTOS_MICROSOFT_WINDOWS3X = 0x02, + HVGUESTOS_MICROSOFT_WINDOWS9X = 0x03, + HVGUESTOS_MICROSOFT_WINDOWSNT = 0x04, + HVGUESTOS_MICROSOFT_WINDOWSCE = 0x05 +}; + +/* + * Declare the MSR used to identify the guest OS. + */ +#define HV_X64_MSR_GUEST_OS_ID 0x40000000 + +union hv_x64_msr_guest_os_id_contents { + u64 as_uint64; + struct { + u64 build_number:16; + u64 service_version:8; /* Service Pack, etc. */ + u64 minor_version:8; + u64 major_version:8; + u64 os_id:8; /* enum hv_guest_os_microsoft_ids (if Vendor=MS) */ + u64 vendor_id:16; /* enum hv_guest_os_vendor */ + }; +}; + +/* + * Declare the MSR used to setup pages used to communicate with the hypervisor. + */ +#define HV_X64_MSR_HYPERCALL 0x40000001 + +union hv_x64_msr_hypercall_contents { + u64 as_uint64; + struct { + u64 enable:1; + u64 reserved:11; + u64 guest_physical_address:52; + }; +}; + + +enum { + VMBUS_MESSAGE_CONNECTION_ID = 1, + VMBUS_MESSAGE_PORT_ID = 1, + VMBUS_EVENT_CONNECTION_ID = 2, + VMBUS_EVENT_PORT_ID = 2, + VMBUS_MONITOR_CONNECTION_ID = 3, + VMBUS_MONITOR_PORT_ID = 3, + VMBUS_MESSAGE_SINT = 2, +}; + +/* #defines */ + +#define HV_PRESENT_BIT 0x80000000 + +#define HV_LINUX_GUEST_ID_LO 0x00000000 +#define HV_LINUX_GUEST_ID_HI 0xB16B00B5 +#define HV_LINUX_GUEST_ID (((u64)HV_LINUX_GUEST_ID_HI << 32) | \ + HV_LINUX_GUEST_ID_LO) + +#define HV_CPU_POWER_MANAGEMENT (1 << 0) +#define HV_RECOMMENDATIONS_MAX 4 + +#define HV_X64_MAX 5 +#define HV_CAPS_MAX 8 + + +#define HV_HYPERCALL_PARAM_ALIGN sizeof(u64) + + +/* Service definitions */ + +#define HV_SERVICE_PARENT_PORT (0) +#define HV_SERVICE_PARENT_CONNECTION (0) + +#define HV_SERVICE_CONNECT_RESPONSE_SUCCESS (0) +#define HV_SERVICE_CONNECT_RESPONSE_INVALID_PARAMETER (1) +#define HV_SERVICE_CONNECT_RESPONSE_UNKNOWN_SERVICE (2) +#define HV_SERVICE_CONNECT_RESPONSE_CONNECTION_REJECTED (3) + +#define HV_SERVICE_CONNECT_REQUEST_MESSAGE_ID (1) +#define HV_SERVICE_CONNECT_RESPONSE_MESSAGE_ID (2) +#define HV_SERVICE_DISCONNECT_REQUEST_MESSAGE_ID (3) +#define HV_SERVICE_DISCONNECT_RESPONSE_MESSAGE_ID (4) +#define HV_SERVICE_MAX_MESSAGE_ID (4) + +#define HV_SERVICE_PROTOCOL_VERSION (0x0010) +#define HV_CONNECT_PAYLOAD_BYTE_COUNT 64 + +/* #define VMBUS_REVISION_NUMBER 6 */ + +/* Our local vmbus's port and connection id. Anything >0 is fine */ +/* #define VMBUS_PORT_ID 11 */ + +/* 628180B8-308D-4c5e-B7DB-1BEB62E62EF4 */ +static const struct hv_guid VMBUS_SERVICE_ID = { + .data = { + 0xb8, 0x80, 0x81, 0x62, 0x8d, 0x30, 0x5e, 0x4c, + 0xb7, 0xdb, 0x1b, 0xeb, 0x62, 0xe6, 0x2e, 0xf4 + }, +}; + +#define MAX_NUM_CPUS 32 + + +struct hv_input_signal_event_buffer { + u64 align8; + struct hv_input_signal_event event; +}; + +struct hv_context { + /* We only support running on top of Hyper-V + * So at this point this really can only contain the Hyper-V ID + */ + u64 guestid; + + void *hypercall_page; + + bool synic_initialized; + + /* + * This is used as an input param to HvCallSignalEvent hypercall. The + * input param is immutable in our usage and must be dynamic mem (vs + * stack or global). */ + struct hv_input_signal_event_buffer *signal_event_buffer; + /* 8-bytes aligned of the buffer above */ + struct hv_input_signal_event *signal_event_param; + + void *synic_message_page[MAX_NUM_CPUS]; + void *synic_event_page[MAX_NUM_CPUS]; +}; + +extern struct hv_context hv_context; + + +/* Hv Interface */ + +extern int hv_init(void); + +extern void hv_cleanup(void); + +extern u16 hv_post_message(union hv_connection_id connection_id, + enum hv_message_type message_type, + void *payload, size_t payload_size); + +extern u16 hv_signal_event(void); + +extern void hv_synic_init(void *irqarg); + +extern void hv_synic_cleanup(void *arg); + + +/* Interface */ + + +int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, void *buffer, + u32 buflen); + +void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info); + +int hv_ringbuffer_write(struct hv_ring_buffer_info *ring_info, + struct scatterlist *sglist, + u32 sgcount); + +int hv_ringbuffer_peek(struct hv_ring_buffer_info *ring_info, void *buffer, + u32 buflen); + +int hv_ringbuffer_read(struct hv_ring_buffer_info *ring_info, + void *buffer, + u32 buflen, + u32 offset); + +u32 hv_get_ringbuffer_interrupt_mask(struct hv_ring_buffer_info *ring_info); + +void hv_dump_ring_info(struct hv_ring_buffer_info *ring_info, char *prefix); + +void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info, + struct hv_ring_buffer_debug_info *debug_info); + +/* + * Maximum channels is determined by the size of the interrupt page + * which is PAGE_SIZE. 1/2 of PAGE_SIZE is for send endpoint interrupt + * and the other is receive endpoint interrupt + */ +#define MAX_NUM_CHANNELS ((PAGE_SIZE >> 1) << 3) /* 16348 channels */ + +/* The value here must be in multiple of 32 */ +/* TODO: Need to make this configurable */ +#define MAX_NUM_CHANNELS_SUPPORTED 256 + + +enum vmbus_connect_state { + DISCONNECTED, + CONNECTING, + CONNECTED, + DISCONNECTING +}; + +#define MAX_SIZE_CHANNEL_MESSAGE HV_MESSAGE_PAYLOAD_BYTE_COUNT + +struct vmbus_connection { + enum vmbus_connect_state conn_state; + + atomic_t next_gpadl_handle; + + /* + * Represents channel interrupts. Each bit position represents a + * channel. When a channel sends an interrupt via VMBUS, it finds its + * bit in the sendInterruptPage, set it and calls Hv to generate a port + * event. The other end receives the port event and parse the + * recvInterruptPage to see which bit is set + */ + void *int_page; + void *send_int_page; + void *recv_int_page; + + /* + * 2 pages - 1st page for parent->child notification and 2nd + * is child->parent notification + */ + void *monitor_pages; + struct list_head chn_msg_list; + spinlock_t channelmsg_lock; + + /* List of channels */ + struct list_head chn_list; + spinlock_t channel_lock; + + struct workqueue_struct *work_queue; +}; + + +struct vmbus_msginfo { + /* Bookkeeping stuff */ + struct list_head msglist_entry; + + /* The message itself */ + unsigned char msg[0]; +}; + + +extern struct vmbus_connection vmbus_connection; + +/* General vmbus interface */ + +struct hv_device *vmbus_child_device_create(struct hv_guid *type, + struct hv_guid *instance, + struct vmbus_channel *channel); + +int vmbus_child_device_register(struct hv_device *child_device_obj); +void vmbus_child_device_unregister(struct hv_device *device_obj); + +/* static void */ +/* VmbusChildDeviceDestroy( */ +/* struct hv_device *); */ + +struct vmbus_channel *relid2channel(u32 relid); + + +/* Connection interface */ + +int vmbus_connect(void); + +int vmbus_post_msg(void *buffer, size_t buflen); + +int vmbus_set_event(u32 child_relid); + +void vmbus_on_event(unsigned long data); + + +#endif /* _HYPERV_VMBUS_H */ diff --git a/drivers/staging/hv/include/asm/hyperv.h b/drivers/staging/hv/include/asm/hyperv.h new file mode 100644 index 0000000..5df477a --- a/dev/null +++ b/drivers/staging/hv/include/asm/hyperv.h @@ -0,0 +1,193 @@ +#ifndef _ASM_X86_HYPERV_H +#define _ASM_X86_HYPERV_H + +#include <linux/types.h> + +/* + * The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent + * is set by CPUID(HvCpuIdFunctionVersionAndFeatures). + */ +#define HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS 0x40000000 +#define HYPERV_CPUID_INTERFACE 0x40000001 +#define HYPERV_CPUID_VERSION 0x40000002 +#define HYPERV_CPUID_FEATURES 0x40000003 +#define HYPERV_CPUID_ENLIGHTMENT_INFO 0x40000004 +#define HYPERV_CPUID_IMPLEMENT_LIMITS 0x40000005 + +#define HYPERV_HYPERVISOR_PRESENT_BIT 0x80000000 +#define HYPERV_CPUID_MIN 0x40000005 +#define HYPERV_CPUID_MAX 0x4000ffff + +/* + * Feature identification. EAX indicates which features are available + * to the partition based upon the current partition privileges. + */ + +/* VP Runtime (HV_X64_MSR_VP_RUNTIME) available */ +#define HV_X64_MSR_VP_RUNTIME_AVAILABLE (1 << 0) +/* Partition Reference Counter (HV_X64_MSR_TIME_REF_COUNT) available*/ +#define HV_X64_MSR_TIME_REF_COUNT_AVAILABLE (1 << 1) +/* + * Basic SynIC MSRs (HV_X64_MSR_SCONTROL through HV_X64_MSR_EOM + * and HV_X64_MSR_SINT0 through HV_X64_MSR_SINT15) available + */ +#define HV_X64_MSR_SYNIC_AVAILABLE (1 << 2) +/* + * Synthetic Timer MSRs (HV_X64_MSR_STIMER0_CONFIG through + * HV_X64_MSR_STIMER3_COUNT) available + */ +#define HV_X64_MSR_SYNTIMER_AVAILABLE (1 << 3) +/* + * APIC access MSRs (HV_X64_MSR_EOI, HV_X64_MSR_ICR and HV_X64_MSR_TPR) + * are available + */ +#define HV_X64_MSR_APIC_ACCESS_AVAILABLE (1 << 4) +/* Hypercall MSRs (HV_X64_MSR_GUEST_OS_ID and HV_X64_MSR_HYPERCALL) available*/ +#define HV_X64_MSR_HYPERCALL_AVAILABLE (1 << 5) +/* Access virtual processor index MSR (HV_X64_MSR_VP_INDEX) available*/ +#define HV_X64_MSR_VP_INDEX_AVAILABLE (1 << 6) +/* Virtual system reset MSR (HV_X64_MSR_RESET) is available*/ +#define HV_X64_MSR_RESET_AVAILABLE (1 << 7) + /* + * Access statistics pages MSRs (HV_X64_MSR_STATS_PARTITION_RETAIL_PAGE, + * HV_X64_MSR_STATS_PARTITION_INTERNAL_PAGE, HV_X64_MSR_STATS_VP_RETAIL_PAGE, + * HV_X64_MSR_STATS_VP_INTERNAL_PAGE) available + */ +#define HV_X64_MSR_STAT_PAGES_AVAILABLE (1 << 8) + +/* + * Feature identification: EBX indicates which flags were specified at + * partition creation. The format is the same as the partition creation + * flag structure defined in section Partition Creation Flags. + */ +#define HV_X64_CREATE_PARTITIONS (1 << 0) +#define HV_X64_ACCESS_PARTITION_ID (1 << 1) +#define HV_X64_ACCESS_MEMORY_POOL (1 << 2) +#define HV_X64_ADJUST_MESSAGE_BUFFERS (1 << 3) +#define HV_X64_POST_MESSAGES (1 << 4) +#define HV_X64_SIGNAL_EVENTS (1 << 5) +#define HV_X64_CREATE_PORT (1 << 6) +#define HV_X64_CONNECT_PORT (1 << 7) +#define HV_X64_ACCESS_STATS (1 << 8) +#define HV_X64_DEBUGGING (1 << 11) +#define HV_X64_CPU_POWER_MANAGEMENT (1 << 12) +#define HV_X64_CONFIGURE_PROFILER (1 << 13) + +/* + * Feature identification. EDX indicates which miscellaneous features + * are available to the partition. + */ +/* The MWAIT instruction is available (per section MONITOR / MWAIT) */ +#define HV_X64_MWAIT_AVAILABLE (1 << 0) +/* Guest debugging support is available */ +#define HV_X64_GUEST_DEBUGGING_AVAILABLE (1 << 1) +/* Performance Monitor support is available*/ +#define HV_X64_PERF_MONITOR_AVAILABLE (1 << 2) +/* Support for physical CPU dynamic partitioning events is available*/ +#define HV_X64_CPU_DYNAMIC_PARTITIONING_AVAILABLE (1 << 3) +/* + * Support for passing hypercall input parameter block via XMM + * registers is available + */ +#define HV_X64_HYPERCALL_PARAMS_XMM_AVAILABLE (1 << 4) +/* Support for a virtual guest idle state is available */ +#define HV_X64_GUEST_IDLE_STATE_AVAILABLE (1 << 5) + +/* + * Implementation recommendations. Indicates which behaviors the hypervisor + * recommends the OS implement for optimal performance. + */ + /* + * Recommend using hypercall for address space switches rather + * than MOV to CR3 instruction + */ +#define HV_X64_MWAIT_RECOMMENDED (1 << 0) +/* Recommend using hypercall for local TLB flushes rather + * than INVLPG or MOV to CR3 instructions */ +#define HV_X64_LOCAL_TLB_FLUSH_RECOMMENDED (1 << 1) +/* + * Recommend using hypercall for remote TLB flushes rather + * than inter-processor interrupts + */ +#define HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED (1 << 2) +/* + * Recommend using MSRs for accessing APIC registers + * EOI, ICR and TPR rather than their memory-mapped counterparts + */ +#define HV_X64_APIC_ACCESS_RECOMMENDED (1 << 3) +/* Recommend using the hypervisor-provided MSR to initiate a system RESET */ +#define HV_X64_SYSTEM_RESET_RECOMMENDED (1 << 4) +/* + * Recommend using relaxed timing for this partition. If used, + * the VM should disable any watchdog timeouts that rely on the + * timely delivery of external interrupts + */ +#define HV_X64_RELAXED_TIMING_RECOMMENDED (1 << 5) + +/* MSR used to identify the guest OS. */ +#define HV_X64_MSR_GUEST_OS_ID 0x40000000 + +/* MSR used to setup pages used to communicate with the hypervisor. */ +#define HV_X64_MSR_HYPERCALL 0x40000001 + +/* MSR used to provide vcpu index */ +#define HV_X64_MSR_VP_INDEX 0x40000002 + +/* MSR used to read the per-partition time reference counter */ +#define HV_X64_MSR_TIME_REF_COUNT 0x40000020 + +/* Define the virtual APIC registers */ +#define HV_X64_MSR_EOI 0x40000070 +#define HV_X64_MSR_ICR 0x40000071 +#define HV_X64_MSR_TPR 0x40000072 +#define HV_X64_MSR_APIC_ASSIST_PAGE 0x40000073 + +/* Define synthetic interrupt controller model specific registers. */ +#define HV_X64_MSR_SCONTROL 0x40000080 +#define HV_X64_MSR_SVERSION 0x40000081 +#define HV_X64_MSR_SIEFP 0x40000082 +#define HV_X64_MSR_SIMP 0x40000083 +#define HV_X64_MSR_EOM 0x40000084 +#define HV_X64_MSR_SINT0 0x40000090 +#define HV_X64_MSR_SINT1 0x40000091 +#define HV_X64_MSR_SINT2 0x40000092 +#define HV_X64_MSR_SINT3 0x40000093 +#define HV_X64_MSR_SINT4 0x40000094 +#define HV_X64_MSR_SINT5 0x40000095 +#define HV_X64_MSR_SINT6 0x40000096 +#define HV_X64_MSR_SINT7 0x40000097 +#define HV_X64_MSR_SINT8 0x40000098 +#define HV_X64_MSR_SINT9 0x40000099 +#define HV_X64_MSR_SINT10 0x4000009A +#define HV_X64_MSR_SINT11 0x4000009B +#define HV_X64_MSR_SINT12 0x4000009C +#define HV_X64_MSR_SINT13 0x4000009D +#define HV_X64_MSR_SINT14 0x4000009E +#define HV_X64_MSR_SINT15 0x4000009F + + +#define HV_X64_MSR_HYPERCALL_ENABLE 0x00000001 +#define HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT 12 +#define HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_MASK \ + (~((1ull << HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT) - 1)) + +/* Declare the various hypercall operations. */ +#define HV_X64_HV_NOTIFY_LONG_SPIN_WAIT 0x0008 + +#define HV_X64_MSR_APIC_ASSIST_PAGE_ENABLE 0x00000001 +#define HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_SHIFT 12 +#define HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_MASK \ + (~((1ull << HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_SHIFT) - 1)) + +#define HV_PROCESSOR_POWER_STATE_C0 0 +#define HV_PROCESSOR_POWER_STATE_C1 1 +#define HV_PROCESSOR_POWER_STATE_C2 2 +#define HV_PROCESSOR_POWER_STATE_C3 3 + +/* hypercall status code */ +#define HV_STATUS_SUCCESS 0 +#define HV_STATUS_INVALID_HYPERCALL_CODE 2 +#define HV_STATUS_INVALID_HYPERCALL_INPUT 3 +#define HV_STATUS_INVALID_ALIGNMENT 4 + +#endif diff --git a/drivers/staging/hv/include/asm/mshyperv.h b/drivers/staging/hv/include/asm/mshyperv.h new file mode 100644 index 0000000..87b5199 --- a/dev/null +++ b/drivers/staging/hv/include/asm/mshyperv.h @@ -0,0 +1,16 @@ +#ifndef _ASM_X86_MSHYPER_H +#define _ASM_X86_MSHYPER_H + +#include <linux/types.h> +#include <asm/hyperv.h> + +struct ms_hyperv_info { + u32 features; + u32 hints; +}; + +extern struct ms_hyperv_info ms_hyperv; +extern void *x86_hyper; +extern int x86_hyper_ms_hyperv; + +#endif diff --git a/drivers/staging/hv/include/linux/atomic.h b/drivers/staging/hv/include/linux/atomic.h new file mode 100644 index 0000000..2e5735a --- a/dev/null +++ b/drivers/staging/hv/include/linux/atomic.h @@ -0,0 +1,5 @@ +#ifndef _LINUX_ATOMIC_H +#define _LINUX_ATOMIC_H +#include <asm/atomic.h> + +#endif diff --git a/drivers/staging/hv/logging.h b/drivers/staging/hv/logging.h deleted file mode 100644 index 20d4d12..0000000 --- a/drivers/staging/hv/logging.h +++ b/dev/null @@ -1,95 +0,0 @@ -/* - * - * 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> - * - */ - - -#ifndef _LOGGING_H_ -#define _LOGGING_H_ - -/* #include <linux/init.h> */ -/* #include <linux/module.h> */ - - -#define VMBUS 0x0001 -#define STORVSC 0x0002 -#define NETVSC 0x0004 -#define INPUTVSC 0x0008 -#define BLKVSC 0x0010 -#define VMBUS_DRV 0x0100 -#define STORVSC_DRV 0x0200 -#define NETVSC_DRV 0x0400 -#define INPUTVSC_DRV 0x0800 -#define BLKVSC_DRV 0x1000 - -#define ALL_MODULES (VMBUS |\ - STORVSC |\ - NETVSC |\ - INPUTVSC |\ - BLKVSC |\ - VMBUS_DRV |\ - STORVSC_DRV |\ - NETVSC_DRV |\ - INPUTVSC_DRV|\ - BLKVSC_DRV) - -/* Logging Level */ -#define ERROR_LVL 3 -#define WARNING_LVL 4 -#define INFO_LVL 6 -#define DEBUG_LVL 7 -#define DEBUG_LVL_ENTEREXIT 8 -#define DEBUG_RING_LVL 9 - -extern unsigned int vmbus_loglevel; - -#define DPRINT(mod, lvl, fmt, args...) do {\ - if ((mod & (HIWORD(vmbus_loglevel))) && \ - (lvl <= LOWORD(vmbus_loglevel))) \ - printk(KERN_DEBUG #mod": %s() " fmt "\n", __func__, ## args);\ - } while (0) - -#define DPRINT_DBG(mod, fmt, args...) do {\ - if ((mod & (HIWORD(vmbus_loglevel))) && \ - (DEBUG_LVL <= LOWORD(vmbus_loglevel))) \ - printk(KERN_DEBUG #mod": %s() " fmt "\n", __func__, ## args);\ - } while (0) - -#define DPRINT_INFO(mod, fmt, args...) do {\ - if ((mod & (HIWORD(vmbus_loglevel))) && \ - (INFO_LVL <= LOWORD(vmbus_loglevel))) \ - printk(KERN_INFO #mod": " fmt "\n", ## args);\ - } while (0) - -#define DPRINT_WARN(mod, fmt, args...) do {\ - if ((mod & (HIWORD(vmbus_loglevel))) && \ - (WARNING_LVL <= LOWORD(vmbus_loglevel))) \ - printk(KERN_WARNING #mod": WARNING! " fmt "\n", ## args);\ - } while (0) - -#define DPRINT_ERR(mod, fmt, args...) do {\ - if ((mod & (HIWORD(vmbus_loglevel))) && \ - (ERROR_LVL <= LOWORD(vmbus_loglevel))) \ - printk(KERN_ERR #mod": %s() ERROR!! " fmt "\n", \ - __func__, ## args);\ - } while (0) - -#endif /* _LOGGING_H_ */ diff --git a/drivers/staging/hv/netvsc.c b/drivers/staging/hv/netvsc.c new file mode 100644 index 0000000..267df74 --- a/dev/null +++ b/drivers/staging/hv/netvsc.c @@ -0,0 +1,1015 @@ +/* + * 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 receiv |