Home Home > GIT Browse > openSUSE-15.0
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlaf Hering <ohering@suse.de>2011-06-15 20:58:57 +0200
committerOlaf Hering <ohering@suse.de>2011-06-15 20:58:57 +0200
commit131ee6958a09a04cdde272d9c67a1089b77638c1 (patch)
tree233282047e890ce5386205c1027147a2be3583d5
parent73a8d173c91e77ddcea56de474c56a906902ff40 (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.
-rw-r--r--config/i386/debug1
-rw-r--r--config/i386/default1
-rw-r--r--config/i386/desktop1
-rw-r--r--config/i386/pae1
-rw-r--r--config/i386/trace1
-rw-r--r--config/x86_64/debug1
-rw-r--r--config/x86_64/default1
-rw-r--r--config/x86_64/desktop1
-rw-r--r--config/x86_64/trace1
-rw-r--r--kernel-source.changes7
-rw-r--r--patches.suse/staging-hv-update-to-3.0.patch28322
-rw-r--r--patches.suse/staging-hyper-v-added-all-patches-for-hyper-v-same-as-2.6.35-now.patch14
-rw-r--r--patches.xen/xen3-patch-2.6.324
-rw-r--r--series.conf1
-rw-r--r--supported.conf1
15 files changed, 28347 insertions, 11 deletions
diff --git a/config/i386/debug b/config/i386/debug
index a514a58d52..fa68e59825 100644
--- a/config/i386/debug
+++ b/config/i386/debug
@@ -4533,6 +4533,7 @@ CONFIG_HYPERV_STORAGE=m
CONFIG_HYPERV_BLOCK=m
CONFIG_HYPERV_NET=m
CONFIG_HYPERV_UTILS=m
+CONFIG_HYPERV_MOUSE=m
CONFIG_VME_BUS=m
#
diff --git a/config/i386/default b/config/i386/default
index 2748996636..73a8d59be6 100644
--- a/config/i386/default
+++ b/config/i386/default
@@ -4504,6 +4504,7 @@ CONFIG_HYPERV_STORAGE=m
CONFIG_HYPERV_BLOCK=m
CONFIG_HYPERV_NET=m
CONFIG_HYPERV_UTILS=m
+CONFIG_HYPERV_MOUSE=m
CONFIG_VME_BUS=m
#
diff --git a/config/i386/desktop b/config/i386/desktop
index 6ef7cf3b73..19440de2a0 100644
--- a/config/i386/desktop
+++ b/config/i386/desktop
@@ -4257,6 +4257,7 @@ CONFIG_HYPERV_STORAGE=m
CONFIG_HYPERV_BLOCK=m
CONFIG_HYPERV_NET=m
CONFIG_HYPERV_UTILS=m
+CONFIG_HYPERV_MOUSE=m
CONFIG_VME_BUS=m
#
diff --git a/config/i386/pae b/config/i386/pae
index c4f84d0d60..69581cc898 100644
--- a/config/i386/pae
+++ b/config/i386/pae
@@ -4343,6 +4343,7 @@ CONFIG_HYPERV_STORAGE=m
CONFIG_HYPERV_BLOCK=m
CONFIG_HYPERV_NET=m
CONFIG_HYPERV_UTILS=m
+CONFIG_HYPERV_MOUSE=m
CONFIG_VME_BUS=m
#
diff --git a/config/i386/trace b/config/i386/trace
index adbde936fe..23c2789eb0 100644
--- a/config/i386/trace
+++ b/config/i386/trace
@@ -4343,6 +4343,7 @@ CONFIG_HYPERV_STORAGE=m
CONFIG_HYPERV_BLOCK=m
CONFIG_HYPERV_NET=m
CONFIG_HYPERV_UTILS=m
+CONFIG_HYPERV_MOUSE=m
CONFIG_VME_BUS=m
#
diff --git a/config/x86_64/debug b/config/x86_64/debug
index 8258a6348a..af1de14186 100644
--- a/config/x86_64/debug
+++ b/config/x86_64/debug
@@ -4277,6 +4277,7 @@ CONFIG_HYPERV_STORAGE=m
CONFIG_HYPERV_BLOCK=m
CONFIG_HYPERV_NET=m
CONFIG_HYPERV_UTILS=m
+CONFIG_HYPERV_MOUSE=m
CONFIG_VME_BUS=m
#
diff --git a/config/x86_64/default b/config/x86_64/default
index 1d89b2c3b0..39f3fdb72d 100644
--- a/config/x86_64/default
+++ b/config/x86_64/default
@@ -4266,6 +4266,7 @@ CONFIG_HYPERV_STORAGE=m
CONFIG_HYPERV_BLOCK=m
CONFIG_HYPERV_NET=m
CONFIG_HYPERV_UTILS=m
+CONFIG_HYPERV_MOUSE=m
CONFIG_VME_BUS=m
#
diff --git a/config/x86_64/desktop b/config/x86_64/desktop
index e06cda4f1d..05b7b8dba6 100644
--- a/config/x86_64/desktop
+++ b/config/x86_64/desktop
@@ -4241,6 +4241,7 @@ CONFIG_HYPERV_STORAGE=m
CONFIG_HYPERV_BLOCK=m
CONFIG_HYPERV_NET=m
CONFIG_HYPERV_UTILS=m
+CONFIG_HYPERV_MOUSE=m
CONFIG_VME_BUS=m
#
diff --git a/config/x86_64/trace b/config/x86_64/trace
index 16fa9c37c5..82fe6d784b 100644
--- a/config/x86_64/trace
+++ b/config/x86_64/trace
@@ -4266,6 +4266,7 @@ CONFIG_HYPERV_STORAGE=m
CONFIG_HYPERV_BLOCK=m
CONFIG_HYPERV_NET=m
CONFIG_HYPERV_UTILS=m
+CONFIG_HYPERV_MOUSE=m
CONFIG_VME_BUS=m
#
diff --git a/kernel-source.changes b/kernel-source.changes
index 77b704e7c7..f72a49311c 100644
--- a/kernel-source.changes
+++ b/kernel-source.changes
@@ -1,4 +1,11 @@
-------------------------------------------------------------------
+Wed Jun 15 20:11:22 CEST 2011 - ohering@suse.de
+
+- patches.suse/staging-hv-update-to-3.0.patch
+ Update hyperv drivers to current mainline state (bnc#662432).
+- patches.xen/xen3-patch-2.6.32: Refresh.
+
+-------------------------------------------------------------------
Wed Jun 15 19:24:35 CEST 2011 - dsterba@suse.cz
- series.conf: move btrfs patches to the proper section
diff --git a/patches.suse/staging-hv-update-to-3.0.patch b/patches.suse/staging-hv-update-to-3.0.patch
new file mode 100644
index 0000000000..436015efc9
--- /dev/null
+++ b/patches.suse/staging-hv-update-to-3.0.patch
@@ -0,0 +1,28322 @@
+From: Olaf Hering <ohering@suse.de>
+Subject: Bug 662432 - TP-L3: Network / DRBD fail in Hyper-V environment
+Patch-mainline: yes
+References: bnc#662432
+
+Update hyperv driver to whatever is currently in Linux 3.
+
+Signed-off-by: Olaf Hering <olaf@aepfle.de>
+
+---
+ drivers/staging/hv/BlkVsc.c | 102 -
+ drivers/staging/hv/Channel.c | 1044 -------------------
+ drivers/staging/hv/Channel.h | 112 --
+ drivers/staging/hv/ChannelInterface.c | 152 --
+ drivers/staging/hv/ChannelInterface.h | 35
+ drivers/staging/hv/ChannelMgmt.c | 851 ----------------
+ drivers/staging/hv/ChannelMgmt.h | 320 ------
+ drivers/staging/hv/Connection.c | 330 ------
+ drivers/staging/hv/Hv.c | 499 ---------
+ drivers/staging/hv/Hv.h | 140 --
+ drivers/staging/hv/Kconfig | 12
+ drivers/staging/hv/Makefile | 17
+ drivers/staging/hv/NetVsc.c | 1328 -------------------------
+ drivers/staging/hv/NetVsc.h | 331 ------
+ drivers/staging/hv/NetVscApi.h | 116 --
+ drivers/staging/hv/RingBuffer.c | 619 -----------
+ drivers/staging/hv/RingBuffer.h | 102 -
+ drivers/staging/hv/RndisFilter.c | 918 -----------------
+ drivers/staging/hv/RndisFilter.h | 55 -
+ drivers/staging/hv/StorVsc.c | 856 ----------------
+ drivers/staging/hv/StorVscApi.h | 110 --
+ drivers/staging/hv/TODO | 5
+ drivers/staging/hv/VersionInfo.h | 48
+ drivers/staging/hv/Vmbus.c | 293 -----
+ drivers/staging/hv/VmbusApi.h | 193 ---
+ drivers/staging/hv/VmbusChannelInterface.h | 89 -
+ drivers/staging/hv/VmbusPacketFormat.h | 161 ---
+ drivers/staging/hv/VmbusPrivate.h | 135 --
+ drivers/staging/hv/blkvsc_drv.c | 1516 +++++++++--------------------
+ drivers/staging/hv/channel.c | 877 ++++++++++++++++
+ drivers/staging/hv/channel_mgmt.c | 784 ++++++++++++++
+ drivers/staging/hv/connection.c | 290 +++++
+ drivers/staging/hv/ext_utils.c | 27
+ drivers/staging/hv/hv.c | 438 ++++++++
+ drivers/staging/hv/hv_api.h | 905 -----------------
+ drivers/staging/hv/hv_compat.h | 81 +
+ drivers/staging/hv/hv_kvp.c | 334 ++++++
+ drivers/staging/hv/hv_kvp.h | 184 +++
+ drivers/staging/hv/hv_mouse.c | 975 ++++++++++++++++++
+ drivers/staging/hv/hv_timesource.c | 64 -
+ drivers/staging/hv/hv_util.c | 306 +++++
+ drivers/staging/hv/hyperv.h | 953 ++++++++++++++++++
+ drivers/staging/hv/hyperv_net.h | 1057 ++++++++++++++++++++
+ drivers/staging/hv/hyperv_storage.h | 334 ++++++
+ drivers/staging/hv/hyperv_utils.c | 307 -----
+ drivers/staging/hv/hyperv_vmbus.h | 629 ++++++++++++
+ drivers/staging/hv/include/asm/hyperv.h | 193 +++
+ drivers/staging/hv/include/asm/mshyperv.h | 16
+ drivers/staging/hv/include/linux/atomic.h | 5
+ drivers/staging/hv/logging.h | 95 -
+ drivers/staging/hv/netvsc.c | 1015 +++++++++++++++++++
+ drivers/staging/hv/netvsc_drv.c | 322 ++----
+ drivers/staging/hv/osd.c | 228 ----
+ drivers/staging/hv/osd.h | 70 -
+ drivers/staging/hv/overrides.mk | 4
+ drivers/staging/hv/ring_buffer.c | 526 ++++++++++
+ drivers/staging/hv/rndis.h | 652 ------------
+ drivers/staging/hv/rndis_filter.c | 831 +++++++++++++++
+ drivers/staging/hv/storvsc.c | 564 ++++++++++
+ drivers/staging/hv/storvsc_drv.c | 1016 ++++++++-----------
+ drivers/staging/hv/tools/hv_kvp_daemon.c | 494 +++++++++
+ drivers/staging/hv/utils.h | 119 --
+ drivers/staging/hv/vmbus.h | 79 -
+ drivers/staging/hv/vmbus_drv.c | 1216 ++++++++++-------------
+ drivers/staging/hv/vstorage.h | 192 ---
+ 65 files changed, 12512 insertions(+), 14159 deletions(-)
+
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/BlkVsc.c
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/BlkVsc.c
++++ /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;
+-}
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/Channel.c
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/Channel.c
++++ /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 ");
+-}
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/Channel.h
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/Channel.h
++++ /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_ */
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/ChannelInterface.c
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/ChannelInterface.c
++++ /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;
+-}
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/ChannelInterface.h
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/ChannelInterface.h
++++ /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_ */
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/ChannelMgmt.c
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/ChannelMgmt.c
++++ /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 */
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/ChannelMgmt.h
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/ChannelMgmt.h
++++ /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_ */
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/Connection.c
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/Connection.c
++++ /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();
+-}
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/Hv.c
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/Hv.c
++++ /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);
+-}
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/Hv.h
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/Hv.h
++++ /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__ */
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/Kconfig
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/Kconfig
++++ linux-2.6.32-SLE11-SP2/drivers/staging/hv/Kconfig
+@@ -1,6 +1,6 @@
+ config HYPERV
+ tristate "Microsoft Hyper-V client drivers"
+- depends on X86 && m
++ depends on X86 && 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
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/Makefile
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/Makefile
++++ linux-2.6.32-SLE11-SP2/drivers/staging/hv/Makefile
+@@ -3,11 +3,14 @@ obj-$(CONFIG_HYPERV_STORAGE) += hv_storv
+ 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
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/NetVsc.c
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/NetVsc.c
++++ /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;
+-}
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/NetVsc.h
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/NetVsc.h
++++ /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_ */
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/NetVscApi.h
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/NetVscApi.h
++++ /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_ */
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/RingBuffer.c
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/RingBuffer.c
++++ /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 */
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/RingBuffer.h
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/RingBuffer.h
++++ /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_ */
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/RndisFilter.c
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/RndisFilter.c
++++ /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 */
+-}
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/RndisFilter.h
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/RndisFilter.h
++++ /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_ */
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/StorVsc.c
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/StorVsc.c
++++ /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;
+-}
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/StorVscApi.h
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/StorVscApi.h
++++ /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_ */
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/TODO
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/TODO
++++ linux-2.6.32-SLE11-SP2/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
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/VersionInfo.h
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/VersionInfo.h
++++ /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
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/Vmbus.c
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/Vmbus.c
++++ /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;
+-}
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/VmbusApi.h
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/VmbusApi.h
++++ /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_ */
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/VmbusChannelInterface.h
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/VmbusChannelInterface.h
++++ /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
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/VmbusPacketFormat.h
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/VmbusPacketFormat.h
++++ /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
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/VmbusPrivate.h
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/VmbusPrivate.h
++++ /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_ */
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/blkvsc_drv.c
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/blkvsc_drv.c
++++ linux-2.6.32-SLE11-SP2/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,450 +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";
++
++/* {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 decl */
+-static int blkvsc_probe(struct device *dev);
+-static int blkvsc_remove(struct device *device);
+-static void blkvsc_shutdown(struct device *device);
+-
+-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 *) &current_dev,
+- blkvsc_drv_exit_cb);
+-
+- if (ret)
+- DPRINT_WARN(BLKVSC_DRV,
+- "driver_for_each_device returned %d", ret);
+-
+-
+- if (current_dev == NULL)
+- break;
+-
+- /* Initiate removal from the top-down */
+- device_unregister(current_dev);
+- }
+-
+- if (storvsc_drv_obj->Base.OnCleanup)
+- storvsc_drv_obj->Base.OnCleanup(&storvsc_drv_obj->Base);
+
+- vmbus_child_driver_unregister(drv_ctx);
+-
+- return;
+-}
++ storvsc_req = &blkvsc_req->request;
++ vm_srb = &storvsc_req->vstor_packet.vm_srb;
+
+-/*
+- * 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;
++ vm_srb->data_in = blkvsc_req->write ? WRITE_TYPE : READ_TYPE;
+
+- 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;
++ storvsc_req->on_io_completion = request_completion;
++ storvsc_req->context = blkvsc_req;
+
+- DPRINT_DBG(BLKVSC_DRV, "blkvsc_probe - enter");
++ 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 */
+
+- if (!storvsc_drv_obj->Base.OnDeviceAdd) {
+- DPRINT_ERR(BLKVSC_DRV, "OnDeviceAdd() not set");
+- ret = -1;
+- goto Cleanup;
+- }
++ vm_srb->cdb_length = blkvsc_req->cmd_len;
+
+- blkdev = kzalloc(sizeof(struct block_device_context), GFP_KERNEL);
+- if (!blkdev) {
+- ret = -ENOMEM;
+- goto Cleanup;
+- }
++ memcpy(vm_srb->cdb, blkvsc_req->cmnd, vm_srb->cdb_length);
+
+- INIT_LIST_HEAD(&blkdev->pending_list);
++ storvsc_req->sense_buffer = blkvsc_req->sense_buffer;
+
+- /* Initialize what we can here */
+- spin_lock_init(&blkdev->lock);
++ ret = storvsc_do_io(blkdev->device_ctx,
++ &blkvsc_req->request);
++ if (ret == 0)
++ blkdev->num_outstanding_reqs++;
+
+- /* ASSERT(sizeof(struct blkvsc_request_group) <= */
+- /* sizeof(struct blkvsc_request)); */
++ return ret;
++}
+
+- 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;
+- }
+
++static int blkvsc_open(struct block_device *bdev, fmode_t mode)
++{
++ struct block_device_context *blkdev = bdev->bd_disk->private_data;
++ unsigned long 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;
+- }
++ spin_lock_irqsave(&blkdev->lock, flags);
+
+- 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;
++ blkdev->users++;
+
+- dev_set_drvdata(device, blkdev);
++ spin_unlock_irqrestore(&blkdev->lock, flags);
+
+- /* 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;
+- }
++ return 0;
++}
+
+- 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_max_phys_segments(blkdev->gd->queue,
+- MAX_MULTIPAGE_BUFFER_COUNT);
+- blk_queue_max_hw_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;
++ 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;
+
+- /*
+- * Set this here since the completion routine may be invoked and
+- * completed before we return
+- */
+- blkvsc_req->cond = 0;
++ case DO_CAPACITY:
++ blkdev->sector_size = 0;
++ blkdev->capacity = 0;
++
++ blkvsc_req->cmnd[0] = READ_CAPACITY;
++ blkvsc_req->cmd_len = 16;
++ blkvsc_req->request.data_buffer.len = 8;
++ break;
++
++ 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;
++ }
+
++ spin_lock_irqsave(&blkdev->lock, flags);
+ blkvsc_submit_request(blkvsc_req, blkvsc_cmd_completion);
++ spin_unlock_irqrestore(&blkdev->lock, flags);
++
++ wait_for_completion_interruptible(&blkvsc_req->request.wait_event);
+
+- DPRINT_DBG(BLKVSC_DRV, "waiting %p to complete - cond %d\n",
+- blkvsc_req, blkvsc_req->cond);
++ /* check error */
++ if (vm_srb->scsi_status) {
++ scsi_normalize_sense(blkvsc_req->sense_buffer,
++ SCSI_SENSE_BUFFERSIZE, &sense_hdr);
+
+- wait_event_interruptible(blkvsc_req->wevent, blkvsc_req->cond);
++ return 0;
++ }
+
+ buf = kmap(page_buf);
+
+- /* print_hex_dump_bytes("", DUMP_PREFIX_NONE, buf, 64); */
+- /* be to le */
+- device_type = buf[0] & 0x1F;
+-
+- 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;
+- }
++ switch (op) {
++ case DO_INQUIRY:
++ device_type = buf[0] & 0x1F;
++
++ if (device_type == 0x0)
++ blkdev->device_type = HARDDISK_TYPE;
++ else
++ blkdev->device_type = UNKNOWN_DEV_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);
++ break;
++
++ case DO_CAPACITY:
++ /* 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];
++ break;
++ default:
++ break;
+
+- 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); */
++cleanup:
+
+ kunmap(page_buf);
+
+@@ -565,200 +424,86 @@ static int blkvsc_do_inquiry(struct bloc
+
+ kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req);
+
+- return 0;
++ return ret;
+ }
+
+-/* 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)
++
++static int blkvsc_cancel_pending_reqs(struct block_device_context *blkdev)
+ {
+- struct blkvsc_request *blkvsc_req;
+- struct page *page_buf;
+- unsigned char *buf;
+- struct scsi_sense_hdr sense_hdr;
++ struct blkvsc_request *pend_req, *tmp;
++ struct blkvsc_request *comp_req, *tmp2;
++ struct vmscsi_request *vm_srb;
+
+- DPRINT_DBG(BLKVSC_DRV, "blkvsc_do_read_capacity()\n");
++ int ret = 0;
+
+- 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;
++ /* 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) {
+
+- 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;
+- }
++ if (comp_req == pend_req)
++ break;
+
+- init_waitqueue_head(&blkvsc_req->wevent);
+- 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 = 8;
+-
+- blkvsc_req->cmnd[0] = READ_CAPACITY;
+- blkvsc_req->cmd_len = 16;
+-
+- /*
+- * 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);
++ list_del(&comp_req->req_entry);
+
+- wait_event_interruptible(blkvsc_req->wevent, 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);
+
+- /* check error */
+- if (blkvsc_req->request.Status) {
+- scsi_normalize_sense(blkvsc_req->sense_buffer,
+- SCSI_SENSE_BUFFERSIZE, &sense_hdr);
++ /* FIXME: shouldn't this do more than return? */
++ if (ret)
++ goto out;
++ }
+
+- 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 = ((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];
+
+- kunmap(page_buf);
+-
+- __free_page(page_buf);
+-
+- kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req);
+-
+- return 0;
+-}
+-
+-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;
+- }
+-
+- init_waitqueue_head(&blkvsc_req->wevent);
+- 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 = 12;
+-
+- blkvsc_req->cmnd[0] = 0x9E; /* READ_CAPACITY16; */
+- blkvsc_req->cmd_len = 16;
+-
+- /*
+- * 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);
++ list_del(&pend_req->pend_entry);
+
+- wait_event_interruptible(blkvsc_req->wevent, blkvsc_req->cond);
++ list_del(&pend_req->req_entry);
+
+- /* 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;
++ 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);
++ }
+ }
+- 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
+-
+- kunmap(page_buf);
+-
+- __free_page(page_buf);
+-
+- 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);
+@@ -767,148 +512,80 @@ static int blkvsc_remove(struct 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)); */
+-
+- blkvsc_req->cmd_len = 16;
+-
+- 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;
+- }
++ struct block_device_context *blkdev = dev_get_drvdata(&dev->device);
++ unsigned long flags;
+
+- blkvsc_req->cmnd[1] |=
+- (blkvsc_req->req->cmd_flags & REQ_FUA) ? 0x8 : 0;
++ if (!blkdev)
++ return;
+
+- *(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;
+- }
++ spin_lock_irqsave(&blkdev->lock, flags);
+
+- blkvsc_req->cmnd[1] |=
+- (blkvsc_req->req->cmd_flags & REQ_FUA) ? 0x8 : 0;
++ blkdev->shutting_down = 1;
+
+- *(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;
+- }
++ blk_stop_queue(blkdev->gd->queue);
+
+- *(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;
+- }
+-}
++ blkvsc_cancel_pending_reqs(blkdev);
+
+-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;
++ spin_unlock_irqrestore(&blkdev->lock, flags);
+
+- 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
++ blkvsc_do_operation(blkdev, DO_FLUSH);
+
+- storvsc_req = &blkvsc_req->request;
+- storvsc_req->Extension = (void *)((unsigned long)blkvsc_req +
+- sizeof(struct blkvsc_request));
++ /*
++ * Now wait for all outgoing I/O to be drained.
++ */
++ storvsc_wait_to_drain((struct storvsc_device *)dev->ext);
+
+- storvsc_req->Type = blkvsc_req->write ? WRITE_TYPE : READ_TYPE;
++}
+
+- storvsc_req->OnIOCompletion = request_completion;
+- storvsc_req->Context = blkvsc_req;
++static int blkvsc_release(struct gendisk *disk, fmode_t mode)
++{
++ struct block_device_context *blkdev = disk->private_data;
++ unsigned long 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 */
++ spin_lock_irqsave(&blkdev->lock, flags);
+
+- storvsc_req->CdbLen = blkvsc_req->cmd_len;
+- storvsc_req->Cdb = blkvsc_req->cmnd;
++ if ((--blkdev->users == 0) && (blkdev->shutting_down)) {
++ blk_stop_queue(blkdev->gd->queue);
++ spin_unlock_irqrestore(&blkdev->lock, flags);
++
++ 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
+@@ -930,11 +607,8 @@ static int blkvsc_do_request(struct bloc
+ 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;
+
+@@ -950,11 +624,6 @@ static int blkvsc_do_request(struct bloc
+ * 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) ||
+@@ -966,10 +635,15 @@ static int blkvsc_do_request(struct bloc
+ (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);
+ }
+
+@@ -977,18 +651,24 @@ static int blkvsc_do_request(struct bloc
+ * 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;
+ }
+
+@@ -997,23 +677,32 @@ static int blkvsc_do_request(struct bloc
+
+ 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;
+
+@@ -1027,10 +716,6 @@ static int blkvsc_do_request(struct bloc
+
+ /* 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));
+@@ -1043,13 +728,6 @@ static int blkvsc_do_request(struct bloc
+
+ 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);
+@@ -1062,186 +740,12 @@ static int blkvsc_do_request(struct bloc
+ &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;
+@@ -1250,8 +754,6 @@ static int blkvsc_do_pending_reqs(struct
+ /* 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);
+@@ -1264,19 +766,17 @@ static int blkvsc_do_pending_reqs(struct
+ 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;
+ }
+@@ -1284,8 +784,6 @@ static void blkvsc_request(struct reques
+ 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;
+ }
+@@ -1294,11 +792,9 @@ static void blkvsc_request(struct reques
+
+ 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;
+@@ -1306,186 +802,216 @@ static void blkvsc_request(struct reques
+ }
+ }
+
+-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;
++ struct block_device_context *blkdev = NULL;
++ struct storvsc_device_info device_info;
++ struct storvsc_major_info major_info;
++ int ret = 0;
++
++ blkdev = kzalloc(sizeof(struct block_device_context), GFP_KERNEL);
++ if (!blkdev) {
++ ret = -ENOMEM;
++ goto cleanup;
++ }
++
++ INIT_LIST_HEAD(&blkdev->pending_list);
+
+- DPRINT_DBG(BLKVSC_DRV, "- enter\n");
++ /* Initialize what we can here */
++ spin_lock_init(&blkdev->lock);
+
+- 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->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;
+ }
+- 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;
+-
+- int sectors_per_track = 0;
+- int heads = 0;
+- int cylinders = 0;
+- int rem = 0;
+-
+- if (total_sectors > (65535 * 16 * 255))
+- total_sectors = (65535 * 16 * 255);
+-
+- if (total_sectors >= (65535 * 16 * 63)) {
+- sectors_per_track = 255;
+- heads = 16;
+-
+- 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)
+@@ -1496,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);
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/channel.c
+===================================================================
+--- /dev/null
++++ linux-2.6.32-SLE11-SP2/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);
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/channel_mgmt.c
+===================================================================
+--- /dev/null
++++ linux-2.6.32-SLE11-SP2/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 */
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/connection.c
+===================================================================
+--- /dev/null
++++ linux-2.6.32-SLE11-SP2/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();
++}
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/ext_utils.c
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/ext_utils.c
++++ /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);
+-}
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/hv.c
+===================================================================
+--- /dev/null
++++ linux-2.6.32-SLE11-SP2/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]);
++}
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/hv_api.h
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/hv_api.h
++++ /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
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/hv_compat.h
+===================================================================
+--- /dev/null
++++ linux-2.6.32-SLE11-SP2/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
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/hv_kvp.c
+===================================================================
+--- /dev/null
++++ linux-2.6.32-SLE11-SP2/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);
++}
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/hv_kvp.h
+===================================================================
+--- /dev/null
++++ linux-2.6.32-SLE11-SP2/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 */
++
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/hv_mouse.c
+===================================================================
+--- /dev/null
++++ linux-2.6.32-SLE11-SP2/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);
++
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/hv_timesource.c
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/hv_timesource.c
++++ linux-2.6.32-SLE11-SP2/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);
+ }
+
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/hv_util.c
+===================================================================
+--- /dev/null
++++ linux-2.6.32-SLE11-SP2/drivers/staging/hv/hv_util.c
+@@ -0,0 +1,306 @@
++/*
++ * 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>
++ */
++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/sysctl.h>
++#include <linux/reboot.h>
++#include <linux/dmi.h>
++#include <linux/pci.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;
++ u32 recvlen;
++ u64 requestid;
++ u8 execute_shutdown = false;
++
++ struct shutdown_msg_data *shutdown_msg;
++
++ struct icmsg_hdr *icmsghdrp;
++ struct icmsg_negotiate *negop = NULL;
++
++ vmbus_recvpacket(channel, shut_txf_buf,
++ PAGE_SIZE, &recvlen, &requestid);
++
++ if (recvlen > 0) {
++ icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[
++ sizeof(struct vmbuspipe_hdr)];
++
++ if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
++ prep_negotiate_resp(icmsghdrp, negop, shut_txf_buf);
++ } else {
++ shutdown_msg =
++ (struct shutdown_msg_data *)&shut_txf_buf[
++ sizeof(struct vmbuspipe_hdr) +
++ sizeof(struct icmsg_hdr)];
++
++ switch (shutdown_msg->flags) {
++ case 0:
++ case 1:
++ icmsghdrp->status = HV_S_OK;
++ execute_shutdown = true;
++
++ pr_info("Shutdown request received -"
++ " graceful shutdown initiated\n");
++ break;
++ default:
++ icmsghdrp->status = HV_E_FAIL;
++ execute_shutdown = false;
++
++ pr_info("Shutdown request received -"
++ " Invalid request\n");
++ break;
++ }
++ }
++
++ icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
++ | ICMSGHDRFLAG_RESPONSE;
++
++ vmbus_sendpacket(channel, shut_txf_buf,
++ recvlen, requestid,
++ VM_PKT_DATA_INBAND, 0);
++ }
++
++ if (execute_shutdown == true)
++ orderly_poweroff(false);
++}
++
++/*
++ * Set guest time to host UTC time.
++ */
++static inline void do_adj_guesttime(u64 hosttime)
++{
++ s64 host_tns;
++ struct timespec host_ts;
++
++ host_tns = (hosttime - WLTIMEDELTA) * 100;
++ host_ts = ns_to_timespec(host_tns);
++
++ do_settimeofday(&host_ts);
++}
++
++/*
++ * Synchronize time with host after reboot, restore, etc.
++ *
++ * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM.
++ * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time
++ * message after the timesync channel is opened. Since the hv_utils module is
++ * loaded after hv_vmbus, the first message is usually missed. The other
++ * thing is, systime is automatically set to emulated hardware clock which may
++ * not be UTC time or in the same time zone. So, to override these effects, we
++ * use the first 50 time samples for initial system time setting.
++ */
++static inline void adj_guesttime(u64 hosttime, u8 flags)
++{
++ static s32 scnt = 50;
++
++ if ((flags & ICTIMESYNCFLAG_SYNC) != 0) {
++ do_adj_guesttime(hosttime);
++ return;
++ }
++
++ if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) {
++ scnt--;
++ do_adj_guesttime(hosttime);
++ }
++}
++
++/*
++ * Time Sync Channel message handler.
++ */
++static void timesync_onchannelcallback(void *context)
++{
++ struct vmbus_channel *channel = context;
++ u32 recvlen;
++ u64 requestid;
++ struct icmsg_hdr *icmsghdrp;
++ struct ictimesync_data *timedatap;
++
++ vmbus_recvpacket(channel, time_txf_buf,
++ PAGE_SIZE, &recvlen, &requestid);
++
++ if (recvlen > 0) {
++ icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[
++ sizeof(struct vmbuspipe_hdr)];
++
++ if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
++ prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf);
++ } else {
++ timedatap = (struct ictimesync_data *)&time_txf_buf[
++ sizeof(struct vmbuspipe_hdr) +
++ sizeof(struct icmsg_hdr)];
++ adj_guesttime(timedatap->parenttime, timedatap->flags);
++ }
++
++ icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
++ | ICMSGHDRFLAG_RESPONSE;
++
++ vmbus_sendpacket(channel, time_txf_buf,
++ recvlen, requestid,
++ VM_PKT_DATA_INBAND, 0);
++ }
++}
++
++/*
++ * Heartbeat functionality.
++ * Every two seconds, Hyper-V send us a heartbeat request message.
++ * we respond to this message, and Hyper-V knows we are alive.
++ */
++static void heartbeat_onchannelcallback(void *context)
++{
++ struct vmbus_channel *channel = context;
++ u32 recvlen;
++ u64 requestid;
++ struct icmsg_hdr *icmsghdrp;
++ struct heartbeat_msg_data *heartbeat_msg;
++
++ vmbus_recvpacket(channel, hbeat_txf_buf,
++ PAGE_SIZE, &recvlen, &requestid);
++
++ if (recvlen > 0) {
++ icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[
++ sizeof(struct vmbuspipe_hdr)];
++
++ if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
++ prep_negotiate_resp(icmsghdrp, NULL, hbeat_txf_buf);
++ } else {
++ heartbeat_msg =
++ (struct heartbeat_msg_data *)&hbeat_txf_buf[
++ sizeof(struct vmbuspipe_hdr) +
++ sizeof(struct icmsg_hdr)];
++
++ heartbeat_msg->seq_num += 1;
++ }
++
++ icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
++ | ICMSGHDRFLAG_RESPONSE;
++
++ vmbus_sendpacket(channel, hbeat_txf_buf,
++ recvlen, requestid,
++ VM_PKT_DATA_INBAND, 0);
++ }
++}
++
++static const struct pci_device_id __initconst
++hv_utils_pci_table[] __maybe_unused = {
++ { PCI_DEVICE(0x1414, 0x5353) }, /* Hyper-V emulated VGA controller */
++ { 0 }
++};
++MODULE_DEVICE_TABLE(pci, hv_utils_pci_table);
++
++
++static const struct dmi_system_id __initconst
++hv_utils_dmi_table[] __maybe_unused = {
++ {
++ .ident = "Hyper-V",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
++ DMI_MATCH(DMI_BOARD_NAME, "Virtual Machine"),
++ },
++ },
++ { },
++};
++MODULE_DEVICE_TABLE(dmi, hv_utils_dmi_table);
++
++
++static int __init init_hyperv_utils(void)
++{
++ 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].callback = &shutdown_onchannelcallback;
++
++ hv_cb_utils[HV_TIMESYNC_MSG].callback = &timesync_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)
++{
++ 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;
++
++ 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_kvp_deinit();
++
++ kfree(shut_txf_buf);
++ kfree(time_txf_buf);
++ kfree(hbeat_txf_buf);
++}
++
++module_init(init_hyperv_utils);
++module_exit(exit_hyperv_utils);
++
++MODULE_DESCRIPTION("Hyper-V Utilities");
++MODULE_VERSION(HV_DRV_VERSION);
++MODULE_LICENSE("GPL");
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/hyperv.h
+===================================================================
+--- /dev/null
++++ linux-2.6.32-SLE11-SP2/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 */
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/hyperv_net.h
+===================================================================
+--- /dev/null
++++ linux-2.6.32-SLE11-SP2/drivers/staging/hv/hyperv_net.h
+@@ -0,0 +1,1057 @@
++/*
++ *
++ * 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_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 */
++
++
++#ifndef STATUS_SUCCESS
++#define STATUS_SUCCESS (0x00000000L)
++#endif
++
++#ifndef STATUS_UNSUCCESSFUL
++#define STATUS_UNSUCCESSFUL (0xC0000001L)
++#endif
++
++#ifndef STATUS_PENDING
++#define STATUS_PENDING (0x00000103L)
++#endif
++
++#ifndef STATUS_INSUFFICIENT_RESOURCES
++#define STATUS_INSUFFICIENT_RESOURCES (0xC000009AL)
++#endif
++
++#ifndef STATUS_BUFFER_OVERFLOW
++#define STATUS_BUFFER_OVERFLOW (0x80000005L)
++#endif
++
++#ifndef STATUS_NOT_SUPPORTED
++#define STATUS_NOT_SUPPORTED (0xC00000BBL)
++#endif
++
++#define RNDIS_STATUS_SUCCESS (STATUS_SUCCESS)
++#define RNDIS_STATUS_PENDING (STATUS_PENDING)
++#define RNDIS_STATUS_NOT_RECOGNIZED (0x00010001L)
++#define RNDIS_STATUS_NOT_COPIED (0x00010002L)
++#define RNDIS_STATUS_NOT_ACCEPTED (0x00010003L)
++#define RNDIS_STATUS_CALL_ACTIVE (0x00010007L)
++
++#define RNDIS_STATUS_ONLINE (0x40010003L)
++#define RNDIS_STATUS_RESET_START (0x40010004L)
++#define RNDIS_STATUS_RESET_END (0x40010005L)
++#define RNDIS_STATUS_RING_STATUS (0x40010006L)
++#define RNDIS_STATUS_CLOSED (0x40010007L)
++#define RNDIS_STATUS_WAN_LINE_UP (0x40010008L)
++#define RNDIS_STATUS_WAN_LINE_DOWN (0x40010009L)
++#define RNDIS_STATUS_WAN_FRAGMENT (0x4001000AL)
++#define RNDIS_STATUS_MEDIA_CONNECT (0x4001000BL)
++#define RNDIS_STATUS_MEDIA_DISCONNECT (0x4001000CL)
++#define RNDIS_STATUS_HARDWARE_LINE_UP (0x4001000DL)
++#define RNDIS_STATUS_HARDWARE_LINE_DOWN (0x4001000EL)
++#define RNDIS_STATUS_INTERFACE_UP (0x4001000FL)
++#define RNDIS_STATUS_INTERFACE_DOWN (0x40010010L)
++#define RNDIS_STATUS_MEDIA_BUSY (0x40010011L)
++#define RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION (0x40010012L)
++#define RNDIS_STATUS_WW_INDICATION RDIA_SPECIFIC_INDICATION
++#define RNDIS_STATUS_LINK_SPEED_CHANGE (0x40010013L)
++
++#define RNDIS_STATUS_NOT_RESETTABLE (0x80010001L)
++#define RNDIS_STATUS_SOFT_ERRORS (0x80010003L)
++#define RNDIS_STATUS_HARD_ERRORS (0x80010004L)
++#define RNDIS_STATUS_BUFFER_OVERFLOW (STATUS_BUFFER_OVERFLOW)
++
++#define RNDIS_STATUS_FAILURE (STATUS_UNSUCCESSFUL)
++#define RNDIS_STATUS_RESOURCES (STATUS_INSUFFICIENT_RESOURCES)
++#define RNDIS_STATUS_CLOSING (0xC0010002L)
++#define RNDIS_STATUS_BAD_VERSION (0xC0010004L)
++#define RNDIS_STATUS_BAD_CHARACTERISTICS (0xC0010005L)
++#define RNDIS_STATUS_ADAPTER_NOT_FOUND (0xC0010006L)
++#define RNDIS_STATUS_OPEN_FAILED (0xC0010007L)
++#define RNDIS_STATUS_DEVICE_FAILED (0xC0010008L)
++#define RNDIS_STATUS_MULTICAST_FULL (0xC0010009L)
++#define RNDIS_STATUS_MULTICAST_EXISTS (0xC001000AL)
++#define RNDIS_STATUS_MULTICAST_NOT_FOUND (0xC001000BL)
++#define RNDIS_STATUS_REQUEST_ABORTED (0xC001000CL)
++#define RNDIS_STATUS_RESET_IN_PROGRESS (0xC001000DL)
++#define RNDIS_STATUS_CLOSING_INDICATING (0xC001000EL)
++#define RNDIS_STATUS_NOT_SUPPORTED (STATUS_NOT_SUPPORTED)
++#define RNDIS_STATUS_INVALID_PACKET (0xC001000FL)
++#define RNDIS_STATUS_OPEN_LIST_FULL (0xC0010010L)
++#define RNDIS_STATUS_ADAPTER_NOT_READY (0xC0010011L)
++#define RNDIS_STATUS_ADAPTER_NOT_OPEN (0xC0010012L)
++#define RNDIS_STATUS_NOT_INDICATING (0xC0010013L)
++#define RNDIS_STATUS_INVALID_LENGTH (0xC0010014L)
++#define RNDIS_STATUS_INVALID_DATA (0xC0010015L)
++#define RNDIS_STATUS_BUFFER_TOO_SHORT (0xC0010016L)
++#define RNDIS_STATUS_INVALID_OID (0xC0010017L)
++#define RNDIS_STATUS_ADAPTER_REMOVED (0xC0010018L)
++#define RNDIS_STATUS_UNSUPPORTED_MEDIA (0xC0010019L)
++#define RNDIS_STATUS_GROUP_ADDRESS_IN_USE (0xC001001AL)
++#define RNDIS_STATUS_FILE_NOT_FOUND (0xC001001BL)
++#define RNDIS_STATUS_ERROR_READING_FILE (0xC001001CL)
++#define RNDIS_STATUS_ALREADY_MAPPED (0xC001001DL)
++#define RNDIS_STATUS_RESOURCE_CONFLICT (0xC001001EL)
++#define RNDIS_STATUS_NO_CABLE (0xC001001FL)
++
++#define RNDIS_STATUS_INVALID_SAP (0xC0010020L)
++#define RNDIS_STATUS_SAP_IN_USE (0xC0010021L)
++#define RNDIS_STATUS_INVALID_ADDRESS (0xC0010022L)
++#define RNDIS_STATUS_VC_NOT_ACTIVATED (0xC0010023L)
++#define RNDIS_STATUS_DEST_OUT_OF_ORDER (0xC0010024L)
++#define RNDIS_STATUS_VC_NOT_AVAILABLE (0xC0010025L)
++#define RNDIS_STATUS_CELLRATE_NOT_AVAILABLE (0xC0010026L)
++#define RNDIS_STATUS_INCOMPATABLE_QOS (0xC0010027L)
++#define RNDIS_STATUS_AAL_PARAMS_UNSUPPORTED (0xC0010028L)
++#define RNDIS_STATUS_NO_ROUTE_TO_DESTINATION (0xC0010029L)
++
++#define RNDIS_STATUS_TOKEN_RING_OPEN_ERROR (0xC0011000L)
++
++/* Object Identifiers used by NdisRequest Query/Set Information */
++/* General Objects */
++#define RNDIS_OID_GEN_SUPPORTED_LIST 0x00010101
++#define RNDIS_OID_GEN_HARDWARE_STATUS 0x00010102
++#define RNDIS_OID_GEN_MEDIA_SUPPORTED 0x00010103
++#define RNDIS_OID_GEN_MEDIA_IN_USE 0x00010104
++#define RNDIS_OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105
++#define RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106
++#define RNDIS_OID_GEN_LINK_SPEED 0x00010107
++#define RNDIS_OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108
++#define RNDIS_OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109
++#define RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A
++#define RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B
++#define RNDIS_OID_GEN_VENDOR_ID 0x0001010C
++#define RNDIS_OID_GEN_VENDOR_DESCRIPTION 0x0001010D
++#define RNDIS_OID_GEN_CURRENT_PACKET_FILTER 0x0001010E
++#define RNDIS_OID_GEN_CURRENT_LOOKAHEAD 0x0001010F
++#define RNDIS_OID_GEN_DRIVER_VERSION 0x00010110
++#define RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111
++#define RNDIS_OID_GEN_PROTOCOL_OPTIONS 0x00010112
++#define RNDIS_OID_GEN_MAC_OPTIONS 0x00010113
++#define RNDIS_OID_GEN_MEDIA_CONNECT_STATUS 0x00010114
++#define RNDIS_OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115
++#define RNDIS_OID_GEN_VENDOR_DRIVER_VERSION 0x00010116
++#define RNDIS_OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118
++#define RNDIS_OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119
++#define RNDIS_OID_GEN_MACHINE_NAME 0x0001021A
++#define RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B
++
++#define RNDIS_OID_GEN_XMIT_OK 0x00020101
++#define RNDIS_OID_GEN_RCV_OK 0x00020102
++#define RNDIS_OID_GEN_XMIT_ERROR 0x00020103
++#define RNDIS_OID_GEN_RCV_ERROR 0x00020104
++#define RNDIS_OID_GEN_RCV_NO_BUFFER 0x00020105
++
++#define RNDIS_OID_GEN_DIRECTED_BYTES_XMIT 0x00020201
++#define RNDIS_OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202
++#define RNDIS_OID_GEN_MULTICAST_BYTES_XMIT 0x00020203
++#define RNDIS_OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204
++#define RNDIS_OID_GEN_BROADCAST_BYTES_XMIT 0x00020205
++#define RNDIS_OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206
++#define RNDIS_OID_GEN_DIRECTED_BYTES_RCV 0x00020207
++#define RNDIS_OID_GEN_DIRECTED_FRAMES_RCV 0x00020208
++#define RNDIS_OID_GEN_MULTICAST_BYTES_RCV 0x00020209
++#define RNDIS_OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A
++#define RNDIS_OID_GEN_BROADCAST_BYTES_RCV 0x0002020B
++#define RNDIS_OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C
++
++#define RNDIS_OID_GEN_RCV_CRC_ERROR 0x0002020D
++#define RNDIS_OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E
++
++#define RNDIS_OID_GEN_GET_TIME_CAPS 0x0002020F
++#define RNDIS_OID_GEN_GET_NETCARD_TIME 0x00020210
++
++/* These are connection-oriented general OIDs. */
++/* These replace the above OIDs for connection-oriented media. */
++#define RNDIS_OID_GEN_CO_SUPPORTED_LIST 0x00010101
++#define RNDIS_OID_GEN_CO_HARDWARE_STATUS 0x00010102
++#define RNDIS_OID_GEN_CO_MEDIA_SUPPORTED 0x00010103
++#define RNDIS_OID_GEN_CO_MEDIA_IN_USE 0x00010104
++#define RNDIS_OID_GEN_CO_LINK_SPEED 0x00010105
++#define RNDIS_OID_GEN_CO_VENDOR_ID 0x00010106
++#define RNDIS_OID_GEN_CO_VENDOR_DESCRIPTION 0x00010107
++#define RNDIS_OID_GEN_CO_DRIVER_VERSION 0x00010108
++#define RNDIS_OID_GEN_CO_PROTOCOL_OPTIONS 0x00010109
++#define RNDIS_OID_GEN_CO_MAC_OPTIONS 0x0001010A
++#define RNDIS_OID_GEN_CO_MEDIA_CONNECT_STATUS 0x0001010B
++#define RNDIS_OID_GEN_CO_VENDOR_DRIVER_VERSION 0x0001010C
++#define RNDIS_OID_GEN_CO_MINIMUM_LINK_SPEED 0x0001010D
++
++#define RNDIS_OID_GEN_CO_GET_TIME_CAPS 0x00010201
++#define RNDIS_OID_GEN_CO_GET_NETCARD_TIME 0x00010202
++
++/* These are connection-oriented statistics OIDs. */
++#define RNDIS_OID_GEN_CO_XMIT_PDUS_OK 0x00020101
++#define RNDIS_OID_GEN_CO_RCV_PDUS_OK 0x00020102
++#define RNDIS_OID_GEN_CO_XMIT_PDUS_ERROR 0x00020103
++#define RNDIS_OID_GEN_CO_RCV_PDUS_ERROR 0x00020104
++#define RNDIS_OID_GEN_CO_RCV_PDUS_NO_BUFFER 0x00020105
++
++
++#define RNDIS_OID_GEN_CO_RCV_CRC_ERROR 0x00020201
++#define RNDIS_OID_GEN_CO_TRANSMIT_QUEUE_LENGTH 0x00020202
++#define RNDIS_OID_GEN_CO_BYTES_XMIT 0x00020203
++#define RNDIS_OID_GEN_CO_BYTES_RCV 0x00020204
++#define RNDIS_OID_GEN_CO_BYTES_XMIT_OUTSTANDING 0x00020205
++#define RNDIS_OID_GEN_CO_NETCARD_LOAD 0x00020206
++
++/* These are objects for Connection-oriented media call-managers. */
++#define RNDIS_OID_CO_ADD_PVC 0xFF000001
++#define RNDIS_OID_CO_DELETE_PVC 0xFF000002
++#define RNDIS_OID_CO_GET_CALL_INFORMATION 0xFF000003
++#define RNDIS_OID_CO_ADD_ADDRESS 0xFF000004
++#define RNDIS_OID_CO_DELETE_ADDRESS 0xFF000005
++#define RNDIS_OID_CO_GET_ADDRESSES 0xFF000006
++#define RNDIS_OID_CO_ADDRESS_CHANGE 0xFF000007
++#define RNDIS_OID_CO_SIGNALING_ENABLED 0xFF000008
++#define RNDIS_OID_CO_SIGNALING_DISABLED 0xFF000009
++
++/* 802.3 Objects (Ethernet) */
++#define RNDIS_OID_802_3_PERMANENT_ADDRESS 0x01010101
++#define RNDIS_OID_802_3_CURRENT_ADDRESS 0x01010102
++#define RNDIS_OID_802_3_MULTICAST_LIST 0x01010103
++#define RNDIS_OID_802_3_MAXIMUM_LIST_SIZE 0x01010104
++#define RNDIS_OID_802_3_MAC_OPTIONS 0x01010105
++
++#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001
++
++#define RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101
++#define RNDIS_OID_802_3_XMIT_ONE_COLLISION 0x01020102
++#define RNDIS_OID_802_3_XMIT_MORE_COLLISIONS 0x01020103
++
++#define RNDIS_OID_802_3_XMIT_DEFERRED 0x01020201
++#define RNDIS_OID_802_3_XMIT_MAX_COLLISIONS 0x01020202
++#define RNDIS_OID_802_3_RCV_OVERRUN 0x01020203
++#define RNDIS_OID_802_3_XMIT_UNDERRUN 0x01020204
++#define RNDIS_OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205
++#define RNDIS_OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206
++#define RNDIS_OID_802_3_XMIT_LATE_COLLISIONS 0x01020207
++
++/* Remote NDIS message types */
++#define REMOTE_NDIS_PACKET_MSG 0x00000001
++#define REMOTE_NDIS_INITIALIZE_MSG 0x00000002
++#define REMOTE_NDIS_HALT_MSG 0x00000003
++#define REMOTE_NDIS_QUERY_MSG 0x00000004
++#define REMOTE_NDIS_SET_MSG 0x00000005
++#define REMOTE_NDIS_RESET_MSG 0x00000006
++#define REMOTE_NDIS_INDICATE_STATUS_MSG 0x00000007
++#define REMOTE_NDIS_KEEPALIVE_MSG 0x00000008
++
++#define REMOTE_CONDIS_MP_CREATE_VC_MSG 0x00008001
++#define REMOTE_CONDIS_MP_DELETE_VC_MSG 0x00008002
++#define REMOTE_CONDIS_MP_ACTIVATE_VC_MSG 0x00008005
++#define REMOTE_CONDIS_MP_DEACTIVATE_VC_MSG 0x00008006
++#define REMOTE_CONDIS_INDICATE_STATUS_MSG 0x00008007
++
++/* Remote NDIS message completion types */
++#define REMOTE_NDIS_INITIALIZE_CMPLT 0x80000002
++#define REMOTE_NDIS_QUERY_CMPLT 0x80000004
++#define REMOTE_NDIS_SET_CMPLT 0x80000005
++#define REMOTE_NDIS_RESET_CMPLT 0x80000006
++#define REMOTE_NDIS_KEEPALIVE_CMPLT 0x80000008
++
++#define REMOTE_CONDIS_MP_CREATE_VC_CMPLT 0x80008001
++#define REMOTE_CONDIS_MP_DELETE_VC_CMPLT 0x80008002
++#define REMOTE_CONDIS_MP_ACTIVATE_VC_CMPLT 0x80008005
++#define REMOTE_CONDIS_MP_DEACTIVATE_VC_CMPLT 0x80008006
++
++/*
++ * Reserved message type for private communication between lower-layer host
++ * driver and remote device, if necessary.
++ */
++#define REMOTE_NDIS_BUS_MSG 0xff000001
++
++/* Defines for DeviceFlags in struct rndis_initialize_complete */
++#define RNDIS_DF_CONNECTIONLESS 0x00000001
++#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002
++#define RNDIS_DF_RAW_DATA 0x00000004
++
++/* Remote NDIS medium types. */
++#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 RNDIS_MEDIUM_MAX 0x0000000d
++
++
++/* Remote NDIS medium connection states. */
++#define RNDIS_MEDIA_STATE_CONNECTED 0x00000000
++#define RNDIS_MEDIA_STATE_DISCONNECTED 0x00000001
++
++/* Remote NDIS version numbers */
++#define RNDIS_MAJOR_VERSION 0x00000001
++#define RNDIS_MINOR_VERSION 0x00000000
++
++
++/* NdisInitialize message */
++struct rndis_initialize_request {
++ u32 req_id;
++ u32 major_ver;
++ u32 minor_ver;
++ u32 max_xfer_size;
++};
++
++/* Response to NdisInitialize */
++struct rndis_initialize_complete {
++ 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 address_family;
++ u32 major_ver;
++ u32 minor_ver;
++};
++
++/* NdisHalt message */
++struct rndis_halt_request {
++ u32 req_id;
++};
++
++/* NdisQueryRequest message */
++struct rndis_query_request {
++ u32 req_id;
++ u32 oid;
++ u32 info_buflen;
++ u32 info_buf_offset;
++ u32 dev_vc_handle;
++};
++
++/* Response to NdisQueryRequest */
++struct rndis_query_complete {
++ u32 req_id;
++ u32 status;
++ u32 info_buflen;
++ u32 info_buf_offset;
++};
++
++/* NdisSetRequest message */
++struct rndis_set_request {
++ u32 req_id;
++ u32 oid;
++ u32 info_buflen;
++ u32 info_buf_offset;
++ u32 dev_vc_handle;
++};
++
++/* Response to NdisSetRequest */
++struct rndis_set_complete {
++ u32 req_id;
++ u32 status;
++};
++
++/* NdisReset message */
++struct rndis_reset_request {
++ u32 reserved;
++};
++
++/* Response to NdisReset */
++struct rndis_reset_complete {
++ u32 status;
++ u32 addressing_reset;
++};
++
++/* NdisMIndicateStatus message */
++struct rndis_indicate_status {
++ 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 diag_status;
++ u32 error_offset;
++};
++
++/* NdisKeepAlive message */
++struct rndis_keepalive_request {
++ u32 req_id;
++};
++
++/* Response to NdisKeepAlive */
++struct rndis_keepalive_complete {
++ u32 req_id;
++ u32 status;
++};
++
++/*
++ * Data message. All Offset fields contain byte offsets from the beginning of
++ * struct rndis_packet. All Length fields are in bytes. VcHandle is set
++ * to 0 for connectionless data, otherwise it contains the VC handle.
++ */
++struct rndis_packet {
++ 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 class_info_offset;
++};
++
++/* Packet extension field contents associated with a Data message. */
++struct rndis_per_packet_info {
++ 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 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 */
++#define RNDIS_CONFIG_PARAM_TYPE_INTEGER 0
++#define RNDIS_CONFIG_PARAM_TYPE_STRING 2
++
++/* CONDIS Miniport messages for connection oriented devices */
++/* that do not implement a call manager. */
++
++/* CoNdisMiniportCreateVc message */
++struct rcondis_mp_create_vc {
++ u32 req_id;
++ u32 ndis_vc_handle;
++};
++
++/* Response to CoNdisMiniportCreateVc */
++struct rcondis_mp_create_vc_complete {
++ u32 req_id;
++ u32 dev_vc_handle;
++ u32 status;
++};
++
++/* CoNdisMiniportDeleteVc message */
++struct rcondis_mp_delete_vc {
++ u32 req_id;
++ u32 dev_vc_handle;
++};
++
++/* Response to CoNdisMiniportDeleteVc */
++struct rcondis_mp_delete_vc_complete {
++ u32 req_id;
++ u32 status;
++};
++
++/* CoNdisMiniportQueryRequest message */
++struct rcondis_mp_query_request {
++ 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 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 ndis_vc_handle;
++ u32 status;
++ u32 status_buflen;
++ u32 status_buf_offset;
++};
++
++/* CONDIS Call/VC parameters */
++struct rcondis_specific_parameters {
++ u32 parameter_type;
++ u32 parameter_length;
++ u32 parameter_lffset;
++};
++
++struct rcondis_media_parameters {
++ u32 flags;
++ u32 reserved1;
++ u32 reserved2;
++ struct rcondis_specific_parameters media_specific;
++};
++
++struct rndis_flowspec {
++ 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 call_mgr_specific;
++};
++
++/* CoNdisMiniportActivateVc message */
++struct rcondis_mp_activate_vc_request {
++ 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 req_id;
++ u32 status;
++};
++
++/* CoNdisMiniportDeactivateVc message */
++struct rcondis_mp_deactivate_vc_request {
++ u32 req_id;
++ u32 flags;
++ u32 dev_vc_handle;
++};
++
++/* Response to CoNdisMiniportDeactivateVc */
++struct rcondis_mp_deactivate_vc_complete {
++ u32 req_id;
++ u32 status;
++};
++
++
++/* union with all of the RNDIS messages */
++union rndis_message_container {
++ 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 ndis_msg_type;
++
++ /* Total length of this message, from the beginning */
++ /* of the sruct rndis_message, in bytes. */
++ u32 msg_len;
++
++ /* Actual 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(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(msg) \
++ (((unsigned char *)(msg)) + msg->info_buf_offset)
++
++/* get pointer to status buffer with message pointer */
++#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(msg) \
++ (((unsigned char *)(msg)) + msg->oob_data_offset)
++
++/* get pointer to data buffer with message pointer */
++#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(rndis_msg) \
++ ((void *) &rndis_msg->msg)
++
++/* get pointer to contained message from NDIS_MESSAGE pointer */
++#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 /* _HYPERV_NET_H */
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/hyperv_storage.h
+===================================================================
+--- /dev/null
++++ linux-2.6.32-SLE11-SP2/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 */
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/hyperv_utils.c
+===================================================================
+--- linux-2.6.32-SLE11-SP2.orig/drivers/staging/hv/hyperv_utils.c
++++ /dev/null
+@@ -1,307 +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/kernel.h>
+-#include <linux/init.h>
+-#include <linux/module.h>
+-#include <linux/slab.h>
+-#include <linux/sysctl.h>
+-#include <linux/reboot.h>
+-#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"
+-
+-
+-static void shutdown_onchannelcallback(void *context)
+-{
+- struct vmbus_channel *channel = context;
+- u8 *buf;
+- u32 buflen, recvlen;
+- u64 requestid;
+- u8 execute_shutdown = false;
+-
+- struct shutdown_msg_data *shutdown_msg;
+-
+- 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) {
+- DPRINT_DBG(VMBUS, "shutdown packet: len=%d, requestid=%lld",
+- recvlen, requestid);
+-
+- icmsghdrp = (struct icmsg_hdr *)&buf[
+- sizeof(struct vmbuspipe_hdr)];
+-
+- if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
+- prep_negotiate_resp(icmsghdrp, negop, buf);
+- } else {
+- shutdown_msg = (struct shutdown_msg_data *)&buf[
+- sizeof(struct vmbuspipe_hdr) +
+- sizeof(struct icmsg_hdr)];
+-
+- switch (shutdown_msg->flags) {
+- case 0:
+- case 1:
+- icmsghdrp->status = HV_S_OK;
+- execute_shutdown = true;
+-
+- DPRINT_INFO(VMBUS, "Shutdown request received -"
+- " gracefull shutdown initiated");
+- break;
+- default:
+- icmsghdrp->status = HV_E_FAIL;
+- execute_shutdown = false;
+-
+- DPRINT_INFO(VMBUS, "Shutdown request received -"
+- " Invalid request");
+- break;
+- };
+- }
+-
+- icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
+- | ICMSGHDRFLAG_RESPONSE;
+-
+- VmbusChannelSendPacket(channel, buf,
+- recvlen, requestid,
+- VmbusPacketTypeDataInBand, 0);
+- }
+-
+- kfree(buf);
+-
+- if (execute_shutdown == true)
+- orderly_poweroff(false);
+-}
+-
+-/*
+- * Set guest time to host UTC time.
+- */
+-static inline void do_adj_guesttime(u64 hosttime)
+-{
+- s64 host_tns;
+- struct timespec host_ts;
+-
+- host_tns = (hosttime - WLTIMEDELTA) * 100;
+- host_ts = ns_to_timespec(host_tns);
+-
+- do_settimeofday(&host_ts);
+-}
+-
+-/*
+- * Synchronize time with host after reboot, restore, etc.
+- *
+- * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM.
+- * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time
+- * message after the timesync channel is opened. Since the hv_utils module is
+- * loaded after hv_vmbus, the first message is usually missed. The other
+- * thing is, systime is automatically set to emulated hardware clock which may
+- * not be UTC time or in the same time zone. So, to override these effects, we
+- * use the first 50 time samples for initial system time setting.
+- */
+-static inline void adj_guesttime(u64 hosttime, u8 flags)
+-{
+- static s32 scnt = 50;
+-
+- if ((flags & ICTIMESYNCFLAG_SYNC) != 0) {
+- do_adj_guesttime(hosttime);
+- return;
+- }
+-
+- if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) {
+- scnt--;
+- do_adj_guesttime(hosttime);
+- }
+-}
+-
+-/*
+- * Time Sync Channel message handler.
+- */
+-static void timesync_onchannelcallback(void *context)
+-{
+- struct vmbus_channel *channel = context;
+- u8 *buf;
+- u32 buflen, 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);
+-
+- if (recvlen > 0) {
+- DPRINT_DBG(VMBUS, "timesync packet: recvlen=%d, requestid=%lld",
+- recvlen, requestid);
+-
+- icmsghdrp = (struct icmsg_hdr *)&buf[
+- sizeof(struct vmbuspipe_hdr)];
+-
+- if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
+- prep_negotiate_resp(icmsghdrp, NULL, buf);
+- } else {
+- timedatap = (struct ictimesync_data *)&buf[
+- sizeof(struct vmbuspipe_hdr) +
+- sizeof(struct icmsg_hdr)];
+- adj_guesttime(timedatap->parenttime, timedatap->flags);
+- }
+-
+- icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
+- | ICMSGHDRFLAG_RESPONSE;
+-
+- VmbusChannelSendPacket(channel, buf,
+- recvlen, requestid,
+- VmbusPacketTypeDataInBand, 0);
+- }
+-
+- kfree(buf);
+-}
+-
+-/*
+- * Heartbeat functionality.
+- * Every two seconds, Hyper-V send us a heartbeat request message.
+- * we respond to this message, and Hyper-V knows we are alive.
+- */
+-static void heartbeat_onchannelcallback(void *context)
+-{
+- struct vmbus_channel *channel = context;
+- u8 *buf;
+- u32 buflen, 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);
+-
+- 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[
+- sizeof(struct vmbuspipe_hdr)];
+-
+- if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
+- prep_negotiate_resp(icmsghdrp, NULL, 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->seq_num += 1;
+- }
+-
+- icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
+- | ICMSGHDRFLAG_RESPONSE;
+-
+- VmbusChannelSendPacket(channel, buf,
+- recvlen, requestid,
+- VmbusPacketTypeDataInBand, 0);
+- }
+-
+- kfree(buf);
+-}
+-
+-static const struct pci_device_id __initconst
+-hv_utils_pci_table[] __maybe_unused = {
+- { PCI_DEVICE(0x1414, 0x5353) }, /* Hyper-V emulated VGA controller */
+- { 0 }
+-};
+-MODULE_DEVICE_TABLE(pci, hv_utils_pci_table);
+-
+-static const struct dmi_system_id __initconst
+-hv_utils_dmi_table[] __maybe_unused = {
+- {
+- .ident = "Hyper-V",
+- .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
+- DMI_MATCH(DMI_BOARD_NAME, "Virtual Machine"),
+- },
+- },
+- { },
+-};
+-MODULE_DEVICE_TABLE(dmi, hv_utils_dmi_table);
+-
+-
+-static int __init init_hyperv_utils(void)
+-{
+- printk(KERN_INFO "Registering HyperV Utility Driver\n");
+-
+- 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 =
+- &timesync_onchannelcallback;
+- hv_cb_utils[HV_TIMESYNC_MSG].callback = &timesync_onchannelcallback;
+-
+- hv_cb_utils[HV_HEARTBEAT_MSG].channel->OnChannelCallback =
+- &heartbeat_onchannelcallback;
+- hv_cb_utils[HV_HEARTBEAT_MSG].callback = &heartbeat_onchannelcallback;
+-
+- return 0;
+-}
+-
+-static void exit_hyperv_utils(void)
+-{
+- printk(KERN_INFO "De-Registered HyperV Utility Driver\n");
+-
+- hv_cb_utils[HV_SHUTDOWN_MSG].channel->OnChannelCallback =
+- &chn_cb_negotiate;
+- hv_cb_utils[HV_SHUTDOWN_MSG].callback = &chn_cb_negotiate;
+-
+- hv_cb_utils[HV_TIMESYNC_MSG].channel->OnChannelCallback =
+- &chn_cb_negotiate;
+- hv_cb_utils[HV_TIMESYNC_MSG].callback = &chn_cb_negotiate;
+-
+- hv_cb_utils[HV_HEARTBEAT_MSG].channel->OnChannelCallback =
+- &chn_cb_negotiate;
+- hv_cb_utils[HV_HEARTBEAT_MSG].callback = &chn_cb_negotiate;
+-}
+-
+-module_init(init_hyperv_utils);
+-module_exit(exit_hyperv_utils);
+-
+-MODULE_DESCRIPTION("Hyper-V Utilities");
+-MODULE_VERSION(HV_DRV_VERSION);
+-MODULE_LICENSE("GPL");
+Index: linux-2.6.32-SLE11-SP2/drivers/staging/hv/hyperv_vmbus.h
+===================================================================
+--- /dev/null
++++ linux-2.6.32-SLE11-SP2/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
<