diff -crN linux-3.8.6-gentoo/arch/x86/include/asm/irq_vectors.h linux-3.8.6-gentoo-hyperv/arch/x86/include/asm/irq_vectors.h
*** linux-3.8.6-gentoo/arch/x86/include/asm/irq_vectors.h	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/arch/x86/include/asm/irq_vectors.h	Sat Apr 13 02:25:13 2013
***************
*** 109,116 ****
  
  #define UV_BAU_MESSAGE			0xf5
  
! /* Xen vector callback to receive events in a HVM domain */
! #define XEN_HVM_EVTCHN_CALLBACK		0xf3
  
  /*
   * Local APIC timer IRQ vector is on a different priority level,
--- 109,115 ----
  
  #define UV_BAU_MESSAGE			0xf5
  
! #define HYPERVISOR_CALLBACK_VECTOR	0xf3
  
  /*
   * Local APIC timer IRQ vector is on a different priority level,
diff -crN linux-3.8.6-gentoo/arch/x86/include/asm/mshyperv.h linux-3.8.6-gentoo-hyperv/arch/x86/include/asm/mshyperv.h
*** linux-3.8.6-gentoo/arch/x86/include/asm/mshyperv.h	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/arch/x86/include/asm/mshyperv.h	Sat Apr 13 02:25:14 2013
***************
*** 3,8 ****
--- 3,9 ----
  
  #include <linux/types.h>
  #include <asm/hyperv.h>
+ #include <linux/interrupt.h>
  
  struct ms_hyperv_info {
  	u32 features;
***************
*** 11,14 ****
--- 12,19 ----
  
  extern struct ms_hyperv_info ms_hyperv;
  
+ void hyperv_callback_vector(void);
+ void hyperv_vector_handler(struct pt_regs *regs);
+ void hv_register_vmbus_handler(int irq, irq_handler_t handler);
+ 
  #endif
diff -crN linux-3.8.6-gentoo/arch/x86/kernel/cpu/mshyperv.c linux-3.8.6-gentoo-hyperv/arch/x86/kernel/cpu/mshyperv.c
*** linux-3.8.6-gentoo/arch/x86/kernel/cpu/mshyperv.c	Sat Apr 13 02:03:28 2013
--- linux-3.8.6-gentoo-hyperv/arch/x86/kernel/cpu/mshyperv.c	Sat Apr 13 02:24:52 2013
***************
*** 14,23 ****
--- 14,28 ----
  #include <linux/time.h>
  #include <linux/clocksource.h>
  #include <linux/module.h>
+ #include <linux/hardirq.h>
+ #include <linux/interrupt.h>
  #include <asm/processor.h>
  #include <asm/hypervisor.h>
  #include <asm/hyperv.h>
  #include <asm/mshyperv.h>
+ #include <asm/desc.h>
+ #include <asm/idle.h>
+ #include <asm/irq_regs.h>
  
  struct ms_hyperv_info ms_hyperv;
  EXPORT_SYMBOL_GPL(ms_hyperv);
***************
*** 30,35 ****
--- 35,47 ----
  	if (!boot_cpu_has(X86_FEATURE_HYPERVISOR))
  		return false;
  
+ 	/*
+ 	 * Xen emulates Hyper-V to support enlightened Windows.
+ 	 * Check to see first if we are on a Xen Hypervisor.
+ 	 */
+ 	if (xen_cpuid_base())
+ 		return false;
+ 
  	cpuid(HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS,
  	      &eax, &hyp_signature[0], &hyp_signature[1], &hyp_signature[2]);
  
***************
*** 70,75 ****
--- 82,93 ----
  
  	if (ms_hyperv.features & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE)
  		clocksource_register_hz(&hyperv_cs, NSEC_PER_SEC/100);
+ #if defined(CONFIG_HYPERV) || defined(CONFIG_HYPERV_MODULE)
+ 	/*
+ 	 * Setup the IDT for hypervisor callback.
+ 	 */
+ 	alloc_intr_gate(HYPERVISOR_CALLBACK_VECTOR, hyperv_callback_vector);
+ #endif
  }
  
  const __refconst struct hypervisor_x86 x86_hyper_ms_hyperv = {
***************
*** 78,80 ****
--- 96,131 ----
  	.init_platform		= ms_hyperv_init_platform,
  };
  EXPORT_SYMBOL(x86_hyper_ms_hyperv);
+ 
+ #if defined(CONFIG_HYPERV) || defined(CONFIG_HYPERV_MODULE)
+ static int vmbus_irq = -1;
+ static irq_handler_t vmbus_isr;
+ 
+ void hv_register_vmbus_handler(int irq, irq_handler_t handler)
+ {
+ 	vmbus_irq = irq;
+ 	vmbus_isr = handler;
+ }
+ 
+ void hyperv_vector_handler(struct pt_regs *regs)
+ {
+ 	struct pt_regs *old_regs = set_irq_regs(regs);
+ 	struct irq_desc *desc;
+ 
+ 	irq_enter();
+ 	exit_idle();
+ 
+ 	desc = irq_to_desc(vmbus_irq);
+ 
+ 	if (desc)
+ 		generic_handle_irq_desc(vmbus_irq, desc);
+ 
+ 	irq_exit();
+ 	set_irq_regs(old_regs);
+ }
+ #else
+ void hv_register_vmbus_handler(int irq, irq_handler_t handler)
+ {
+ }
+ #endif
+ EXPORT_SYMBOL_GPL(hv_register_vmbus_handler);
diff -crN linux-3.8.6-gentoo/arch/x86/kernel/entry_32.S linux-3.8.6-gentoo-hyperv/arch/x86/kernel/entry_32.S
*** linux-3.8.6-gentoo/arch/x86/kernel/entry_32.S	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/arch/x86/kernel/entry_32.S	Sat Apr 13 02:12:27 2013
***************
*** 1091,1101 ****
  	_ASM_EXTABLE(4b,9b)
  ENDPROC(xen_failsafe_callback)
  
! BUILD_INTERRUPT3(xen_hvm_callback_vector, XEN_HVM_EVTCHN_CALLBACK,
  		xen_evtchn_do_upcall)
  
  #endif	/* CONFIG_XEN */
  
  #ifdef CONFIG_FUNCTION_TRACER
  #ifdef CONFIG_DYNAMIC_FTRACE
  
--- 1091,1108 ----
  	_ASM_EXTABLE(4b,9b)
  ENDPROC(xen_failsafe_callback)
  
! BUILD_INTERRUPT3(xen_hvm_callback_vector, HYPERVISOR_CALLBACK_VECTOR,
  		xen_evtchn_do_upcall)
  
  #endif	/* CONFIG_XEN */
  
+ #if IS_ENABLED(CONFIG_HYPERV)
+ 
+ BUILD_INTERRUPT3(hyperv_callback_vector, HYPERVISOR_CALLBACK_VECTOR,
+ 		hyperv_vector_handler)
+ 
+ #endif /* CONFIG_HYPERV */
+ 
  #ifdef CONFIG_FUNCTION_TRACER
  #ifdef CONFIG_DYNAMIC_FTRACE
  
diff -crN linux-3.8.6-gentoo/arch/x86/kernel/entry_64.S linux-3.8.6-gentoo-hyperv/arch/x86/kernel/entry_64.S
*** linux-3.8.6-gentoo/arch/x86/kernel/entry_64.S	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/arch/x86/kernel/entry_64.S	Sat Apr 13 02:12:27 2013
***************
*** 1454,1464 ****
  	CFI_ENDPROC
  END(xen_failsafe_callback)
  
! apicinterrupt XEN_HVM_EVTCHN_CALLBACK \
  	xen_hvm_callback_vector xen_evtchn_do_upcall
  
  #endif /* CONFIG_XEN */
  
  /*
   * Some functions should be protected against kprobes
   */
--- 1454,1471 ----
  	CFI_ENDPROC
  END(xen_failsafe_callback)
  
! apicinterrupt HYPERVISOR_CALLBACK_VECTOR \
  	xen_hvm_callback_vector xen_evtchn_do_upcall
  
  #endif /* CONFIG_XEN */
  
+ #if IS_ENABLED(CONFIG_HYPERV)
+ 
+ apicinterrupt HYPERVISOR_CALLBACK_VECTOR \
+ 	hyperv_callback_vector hyperv_vector_handler
+ 
+ #endif /* CONFIG_HYPERV */
+ 
  /*
   * Some functions should be protected against kprobes
   */
diff -crN linux-3.8.6-gentoo/drivers/hid/hid-hyperv.c linux-3.8.6-gentoo-hyperv/drivers/hid/hid-hyperv.c
*** linux-3.8.6-gentoo/drivers/hid/hid-hyperv.c	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/drivers/hid/hid-hyperv.c	Sat Apr 13 02:24:31 2013
***************
*** 568,575 ****
  
  static const struct hv_vmbus_device_id id_table[] = {
  	/* Mouse guid */
! 	{ VMBUS_DEVICE(0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c,
! 		       0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A) },
  	{ },
  };
  
--- 568,574 ----
  
  static const struct hv_vmbus_device_id id_table[] = {
  	/* Mouse guid */
! 	{ HV_MOUSE_GUID, },
  	{ },
  };
  
***************
*** 594,598 ****
--- 593,599 ----
  
  MODULE_LICENSE("GPL");
  MODULE_VERSION(HV_DRV_VERSION);
+ /* bind to old driver to simplify upgrade path */
+ MODULE_ALIAS("hv_mouse");
  module_init(mousevsc_init);
  module_exit(mousevsc_exit);
diff -crN linux-3.8.6-gentoo/drivers/hv/Kconfig linux-3.8.6-gentoo-hyperv/drivers/hv/Kconfig
*** linux-3.8.6-gentoo/drivers/hv/Kconfig	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/drivers/hv/Kconfig	Sat Apr 13 02:25:44 2013
***************
*** 2,8 ****
  
  config HYPERV
  	tristate "Microsoft Hyper-V client drivers"
! 	depends on X86 && ACPI && PCI
  	help
  	  Select this option to run Linux as a Hyper-V client operating
  	  system.
--- 2,8 ----
  
  config HYPERV
  	tristate "Microsoft Hyper-V client drivers"
! 	depends on X86 && ACPI && PCI && X86_LOCAL_APIC && !XEN
  	help
  	  Select this option to run Linux as a Hyper-V client operating
  	  system.
***************
*** 15,21 ****
  
  config HYPERV_BALLOON
  	tristate "Microsoft Hyper-V Balloon driver"
! 	depends on HYPERV
  	help
  	  Select this option to enable Hyper-V Balloon driver.
  
--- 15,21 ----
  
  config HYPERV_BALLOON
  	tristate "Microsoft Hyper-V Balloon driver"
! 	depends on HYPERV = y
  	help
  	  Select this option to enable Hyper-V Balloon driver.
  
diff -crN linux-3.8.6-gentoo/drivers/hv/Makefile linux-3.8.6-gentoo-hyperv/drivers/hv/Makefile
*** linux-3.8.6-gentoo/drivers/hv/Makefile	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/drivers/hv/Makefile	Sat Apr 13 02:25:32 2013
***************
*** 5,8 ****
  hv_vmbus-y := vmbus_drv.o \
  		 hv.o connection.o channel.o \
  		 channel_mgmt.o ring_buffer.o
! hv_utils-y := hv_util.o hv_kvp.o
--- 5,8 ----
  hv_vmbus-y := vmbus_drv.o \
  		 hv.o connection.o channel.o \
  		 channel_mgmt.o ring_buffer.o
! hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o
diff -crN linux-3.8.6-gentoo/drivers/hv/channel.c linux-3.8.6-gentoo-hyperv/drivers/hv/channel.c
*** linux-3.8.6-gentoo/drivers/hv/channel.c	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/drivers/hv/channel.c	Sat Apr 13 02:24:31 2013
***************
*** 55,61 ****
  					[channel->monitor_grp].pending);
  
  	} else {
! 		vmbus_set_event(channel->offermsg.child_relid);
  	}
  }
  
--- 55,61 ----
  					[channel->monitor_grp].pending);
  
  	} else {
! 		vmbus_set_event(channel);
  	}
  }
  
***************
*** 181,187 ****
  	open_msg->ringbuffer_gpadlhandle = newchannel->ringbuffer_gpadlhandle;
  	open_msg->downstream_ringbuffer_pageoffset = send_ringbuffer_size >>
  						  PAGE_SHIFT;
! 	open_msg->server_contextarea_gpadlhandle = 0;
  
  	if (userdatalen > MAX_USER_DEFINED_BYTES) {
  		err = -EINVAL;
--- 181,187 ----
  	open_msg->ringbuffer_gpadlhandle = newchannel->ringbuffer_gpadlhandle;
  	open_msg->downstream_ringbuffer_pageoffset = send_ringbuffer_size >>
  						  PAGE_SHIFT;
! 	open_msg->target_vp = newchannel->target_vp;
  
  	if (userdatalen > MAX_USER_DEFINED_BYTES) {
  		err = -EINVAL;
***************
*** 564,569 ****
--- 564,570 ----
  	struct scatterlist bufferlist[3];
  	u64 aligned_data = 0;
  	int ret;
+ 	bool signal = false;
  
  
  	/* Setup the descriptor */
***************
*** 580,588 ****
  	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;
--- 581,589 ----
  	sg_set_buf(&bufferlist[2], &aligned_data,
  		   packetlen_aligned - packetlen);
  
! 	ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal);
  
! 	if (ret == 0 && signal)
  		vmbus_setevent(channel);
  
  	return ret;
***************
*** 606,611 ****
--- 607,613 ----
  	u32 packetlen_aligned;
  	struct scatterlist bufferlist[3];
  	u64 aligned_data = 0;
+ 	bool signal = false;
  
  	if (pagecount > MAX_PAGE_BUFFER_COUNT)
  		return -EINVAL;
***************
*** 641,649 ****
  	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;
--- 643,651 ----
  	sg_set_buf(&bufferlist[2], &aligned_data,
  		packetlen_aligned - packetlen);
  
! 	ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal);
  
! 	if (ret == 0 && signal)
  		vmbus_setevent(channel);
  
  	return ret;
***************
*** 665,670 ****
--- 667,673 ----
  	u32 packetlen_aligned;
  	struct scatterlist bufferlist[3];
  	u64 aligned_data = 0;
+ 	bool signal = false;
  	u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset,
  					 multi_pagebuffer->len);
  
***************
*** 703,711 ****
  	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;
--- 706,714 ----
  	sg_set_buf(&bufferlist[2], &aligned_data,
  		packetlen_aligned - packetlen);
  
! 	ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal);
  
! 	if (ret == 0 && signal)
  		vmbus_setevent(channel);
  
  	return ret;
***************
*** 732,737 ****
--- 735,741 ----
  	u32 packetlen;
  	u32 userlen;
  	int ret;
+ 	bool signal = false;
  
  	*buffer_actual_len = 0;
  	*requestid = 0;
***************
*** 758,765 ****
  
  	/* Copy over the packet to the user buffer */
  	ret = hv_ringbuffer_read(&channel->inbound, buffer, userlen,
! 			     (desc.offset8 << 3));
  
  
  	return 0;
  }
--- 762,771 ----
  
  	/* Copy over the packet to the user buffer */
  	ret = hv_ringbuffer_read(&channel->inbound, buffer, userlen,
! 			     (desc.offset8 << 3), &signal);
  
+ 	if (signal)
+ 		vmbus_setevent(channel);
  
  	return 0;
  }
***************
*** 774,781 ****
  {
  	struct vmpacket_descriptor desc;
  	u32 packetlen;
- 	u32 userlen;
  	int ret;
  
  	*buffer_actual_len = 0;
  	*requestid = 0;
--- 780,787 ----
  {
  	struct vmpacket_descriptor desc;
  	u32 packetlen;
  	int ret;
+ 	bool signal = false;
  
  	*buffer_actual_len = 0;
  	*requestid = 0;
***************
*** 788,794 ****
  
  
  	packetlen = desc.len8 << 3;
- 	userlen = packetlen - (desc.offset8 << 3);
  
  	*buffer_actual_len = packetlen;
  
--- 794,799 ----
***************
*** 802,808 ****
  	*requestid = desc.trans_id;
  
  	/* Copy over the entire packet to the user buffer */
! 	ret = hv_ringbuffer_read(&channel->inbound, buffer, packetlen, 0);
  
  	return 0;
  }
--- 807,817 ----
  	*requestid = desc.trans_id;
  
  	/* Copy over the entire packet to the user buffer */
! 	ret = hv_ringbuffer_read(&channel->inbound, buffer, packetlen, 0,
! 				 &signal);
! 
! 	if (signal)
! 		vmbus_setevent(channel);
  
  	return 0;
  }
diff -crN linux-3.8.6-gentoo/drivers/hv/channel_mgmt.c linux-3.8.6-gentoo-hyperv/drivers/hv/channel_mgmt.c
*** linux-3.8.6-gentoo/drivers/hv/channel_mgmt.c	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/drivers/hv/channel_mgmt.c	Sat Apr 13 02:24:31 2013
***************
*** 165,172 ****
--- 165,183 ----
  	struct vmbus_channel *channel = container_of(work,
  						     struct vmbus_channel,
  						     work);
+ 	unsigned long flags;
+ 	struct vmbus_channel_relid_released msg;
  
  	vmbus_device_unregister(channel->device_obj);
+ 	memset(&msg, 0, sizeof(struct vmbus_channel_relid_released));
+ 	msg.child_relid = channel->offermsg.child_relid;
+ 	msg.header.msgtype = CHANNELMSG_RELID_RELEASED;
+ 	vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released));
+ 
+ 	spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
+ 	list_del(&channel->listentry);
+ 	spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
+ 	free_channel(channel);
  }
  
  void vmbus_free_channels(void)
***************
*** 257,262 ****
--- 268,337 ----
  	}
  }
  
+ enum {
+ 	IDE = 0,
+ 	SCSI,
+ 	NIC,
+ 	MAX_PERF_CHN,
+ };
+ 
+ /*
+  * This is an array of device_ids (device types) that are performance critical.
+  * We attempt to distribute the interrupt load for these devices across
+  * all available CPUs.
+  */
+ static const struct hv_vmbus_device_id hp_devs[] = {
+ 	/* IDE */
+ 	{ HV_IDE_GUID, },
+ 	/* Storage - SCSI */
+ 	{ HV_SCSI_GUID, },
+ 	/* Network */
+ 	{ HV_NIC_GUID, },
+ };
+ 
+ 
+ /*
+  * We use this state to statically distribute the channel interrupt load.
+  */
+ static u32  next_vp;
+ 
+ /*
+  * Starting with Win8, we can statically distribute the incoming
+  * channel interrupt load by binding a channel to VCPU. We
+  * implement here a simple round robin scheme for distributing
+  * the interrupt load.
+  * We will bind channels that are not performance critical to cpu 0 and
+  * performance critical channels (IDE, SCSI and Network) will be uniformly
+  * distributed across all available CPUs.
+  */
+ static u32 get_vp_index(uuid_le *type_guid)
+ {
+ 	u32 cur_cpu;
+ 	int i;
+ 	bool perf_chn = false;
+ 	u32 max_cpus = num_online_cpus();
+ 
+ 	for (i = IDE; i < MAX_PERF_CHN; i++) {
+ 		if (!memcmp(type_guid->b, hp_devs[i].guid,
+ 				 sizeof(uuid_le))) {
+ 			perf_chn = true;
+ 			break;
+ 		}
+ 	}
+ 	if ((vmbus_proto_version == VERSION_WS2008) ||
+ 	    (vmbus_proto_version == VERSION_WIN7) || (!perf_chn)) {
+ 		/*
+ 		 * Prior to win8, all channel interrupts are
+ 		 * delivered on cpu 0.
+ 		 * Also if the channel is not a performance critical
+ 		 * channel, bind it to cpu 0.
+ 		 */
+ 		return 0;
+ 	}
+ 	cur_cpu = (++next_vp % max_cpus);
+ 	return cur_cpu;
+ }
+ 
  /*
   * vmbus_onoffer - Handler for channel offers from vmbus in parent partition.
   *
***************
*** 275,280 ****
--- 350,384 ----
  		return;
  	}
  
+ 	/*
+ 	 * By default we setup state to enable batched
+ 	 * reading. A specific service can choose to
+ 	 * disable this prior to opening the channel.
+ 	 */
+ 	newchannel->batched_reading = true;
+ 
+ 	/*
+ 	 * Setup state for signalling the host.
+ 	 */
+ 	newchannel->sig_event = (struct hv_input_signal_event *)
+ 				(ALIGN((unsigned long)
+ 				&newchannel->sig_buf,
+ 				HV_HYPERCALL_PARAM_ALIGN));
+ 
+ 	newchannel->sig_event->connectionid.asu32 = 0;
+ 	newchannel->sig_event->connectionid.u.id = VMBUS_EVENT_CONNECTION_ID;
+ 	newchannel->sig_event->flag_number = 0;
+ 	newchannel->sig_event->rsvdz = 0;
+ 
+ 	if (vmbus_proto_version != VERSION_WS2008) {
+ 		newchannel->is_dedicated_interrupt =
+ 				(offer->is_dedicated_interrupt != 0);
+ 		newchannel->sig_event->connectionid.u.id =
+ 				offer->connection_id;
+ 	}
+ 
+ 	newchannel->target_vp = get_vp_index(&offer->offer.if_type);
+ 
  	memcpy(&newchannel->offermsg, offer,
  	       sizeof(struct vmbus_channel_offer_channel));
  	newchannel->monitor_grp = (u8)offer->monitorid / 32;
diff -crN linux-3.8.6-gentoo/drivers/hv/connection.c linux-3.8.6-gentoo-hyperv/drivers/hv/connection.c
*** linux-3.8.6-gentoo/drivers/hv/connection.c	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/drivers/hv/connection.c	Sat Apr 13 02:24:31 2013
***************
*** 30,35 ****
--- 30,36 ----
  #include <linux/slab.h>
  #include <linux/vmalloc.h>
  #include <linux/hyperv.h>
+ #include <linux/module.h>
  #include <asm/hyperv.h>
  #include "hyperv_vmbus.h"
  
***************
*** 40,54 ****
  };
  
  /*
   * 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;
  
  	/* Initialize the vmbus connection */
  	vmbus_connection.conn_state = CONNECTING;
--- 41,139 ----
  };
  
  /*
+  * Negotiated protocol version with the host.
+  */
+ __u32 vmbus_proto_version;
+ EXPORT_SYMBOL_GPL(vmbus_proto_version);
+ 
+ static __u32 vmbus_get_next_version(__u32 current_version)
+ {
+ 	switch (current_version) {
+ 	case (VERSION_WIN7):
+ 		return VERSION_WS2008;
+ 
+ 	case (VERSION_WIN8):
+ 		return VERSION_WIN7;
+ 
+ 	case (VERSION_WS2008):
+ 	default:
+ 		return VERSION_INVAL;
+ 	}
+ }
+ 
+ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo,
+ 					__u32 version)
+ {
+ 	int ret = 0;
+ 	struct vmbus_channel_initiate_contact *msg;
+ 	unsigned long flags;
+ 	int t;
+ 
+ 	init_completion(&msginfo->waitevent);
+ 
+ 	msg = (struct vmbus_channel_initiate_contact *)msginfo->msg;
+ 
+ 	msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT;
+ 	msg->vmbus_version_requested = version;
+ 	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);
+ 		return ret;
+ 	}
+ 
+ 	/* Wait for the connection response */
+ 	t =  wait_for_completion_timeout(&msginfo->waitevent, 5*HZ);
+ 	if (t == 0) {
+ 		spin_lock_irqsave(&vmbus_connection.channelmsg_lock,
+ 				flags);
+ 		list_del(&msginfo->msglistentry);
+ 		spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock,
+ 					flags);
+ 		return -ETIMEDOUT;
+ 	}
+ 
+ 	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 {
+ 		return -ECONNREFUSED;
+ 	}
+ 
+ 	return ret;
+ }
+ 
+ /*
   * vmbus_connect - Sends a connect request on the partition service connection
   */
  int vmbus_connect(void)
  {
  	int ret = 0;
  	struct vmbus_channel_msginfo *msginfo = NULL;
! 	__u32 version;
  
  	/* Initialize the vmbus connection */
  	vmbus_connection.conn_state = CONNECTING;
***************
*** 99,167 ****
  		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, 5*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)
--- 184,221 ----
  		goto cleanup;
  	}
  
  	/*
! 	 * Negotiate a compatible VMBUS version number with the
! 	 * host. We start with the highest number we can support
! 	 * and work our way down until we negotiate a compatible
! 	 * version.
  	 */
  
! 	version = VERSION_CURRENT;
  
! 	do {
! 		ret = vmbus_negotiate_version(msginfo, version);
! 		if (ret == 0)
! 			break;
  
! 		version = vmbus_get_next_version(version);
! 	} while (version != VERSION_INVAL);
  
! 	if (version == VERSION_INVAL)
  		goto cleanup;
! 
! 	vmbus_proto_version = version;
! 	pr_info("Hyper-V Host Build:%d-%d.%d-%d-%d.%d; Vmbus version:%d.%d\n",
! 		    host_info_eax, host_info_ebx >> 16,
! 		    host_info_ebx & 0xFFFF, host_info_ecx,
! 		    host_info_edx >> 24, host_info_edx & 0xFFFFFF,
! 		    version >> 16, version & 0xFFFF);
  
  	kfree(msginfo);
  	return 0;
  
  cleanup:
+ 	pr_err("Unable to connect to host\n");
  	vmbus_connection.conn_state = DISCONNECTED;
  
  	if (vmbus_connection.work_queue)
***************
*** 212,217 ****
--- 266,274 ----
  {
  	struct vmbus_channel *channel;
  	unsigned long flags;
+ 	void *arg;
+ 	bool read_state;
+ 	u32 bytes_to_read;
  
  	/*
  	 * Find the channel based on this relid and invokes the
***************
*** 234,243 ****
  	 */
  
  	spin_lock_irqsave(&channel->inbound_lock, flags);
! 	if (channel->onchannel_callback != NULL)
! 		channel->onchannel_callback(channel->channel_callback_context);
! 	else
  		pr_err("no channel callback for relid - %u\n", relid);
  
  	spin_unlock_irqrestore(&channel->inbound_lock, flags);
  }
--- 291,319 ----
  	 */
  
  	spin_lock_irqsave(&channel->inbound_lock, flags);
! 	if (channel->onchannel_callback != NULL) {
! 		arg = channel->channel_callback_context;
! 		read_state = channel->batched_reading;
! 		/*
! 		 * This callback reads the messages sent by the host.
! 		 * We can optimize host to guest signaling by ensuring:
! 		 * 1. While reading the channel, we disable interrupts from
! 		 *    host.
! 		 * 2. Ensure that we process all posted messages from the host
! 		 *    before returning from this callback.
! 		 * 3. Once we return, enable signaling from the host. Once this
! 		 *    state is set we check to see if additional packets are
! 		 *    available to read. In this case we repeat the process.
! 		 */
! 
! 		do {
! 			hv_begin_read(&channel->inbound);
! 			channel->onchannel_callback(arg);
! 			bytes_to_read = hv_end_read(&channel->inbound);
! 		} while (read_state && (bytes_to_read != 0));
! 	} else {
  		pr_err("no channel callback for relid - %u\n", relid);
+ 	}
  
  	spin_unlock_irqrestore(&channel->inbound_lock, flags);
  }
***************
*** 248,257 ****
  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)
--- 324,355 ----
  void vmbus_on_event(unsigned long data)
  {
  	u32 dword;
! 	u32 maxdword;
  	int bit;
  	u32 relid;
! 	u32 *recv_int_page = NULL;
! 	void *page_addr;
! 	int cpu = smp_processor_id();
! 	union hv_synic_event_flags *event;
! 
! 	if ((vmbus_proto_version == VERSION_WS2008) ||
! 		(vmbus_proto_version == VERSION_WIN7)) {
! 		maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5;
! 		recv_int_page = vmbus_connection.recv_int_page;
! 	} else {
! 		/*
! 		 * When the host is win8 and beyond, the event page
! 		 * can be directly checked to get the id of the channel
! 		 * that has the interrupt pending.
! 		 */
! 		maxdword = HV_EVENT_FLAGS_DWORD_COUNT;
! 		page_addr = hv_context.synic_event_page[cpu];
! 		event = (union hv_synic_event_flags *)page_addr +
! 						 VMBUS_MESSAGE_SINT;
! 		recv_int_page = event->flags32;
! 	}
! 
! 
  
  	/* Check events */
  	if (!recv_int_page)
***************
*** 307,318 ****
  /*
   * 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();
  }
--- 405,420 ----
  /*
   * vmbus_set_event - Send an event notification to the parent
   */
! int vmbus_set_event(struct vmbus_channel *channel)
  {
! 	u32 child_relid = channel->offermsg.child_relid;
! 
! 	if (!channel->is_dedicated_interrupt) {
! 		/* 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(channel->sig_event);
  }
diff -crN linux-3.8.6-gentoo/drivers/hv/hv.c linux-3.8.6-gentoo-hyperv/drivers/hv/hv.c
*** linux-3.8.6-gentoo/drivers/hv/hv.c	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/drivers/hv/hv.c	Sat Apr 13 02:24:31 2013
***************
*** 27,32 ****
--- 27,33 ----
  #include <linux/vmalloc.h>
  #include <linux/hyperv.h>
  #include <linux/version.h>
+ #include <linux/interrupt.h>
  #include <asm/hyperv.h>
  #include "hyperv_vmbus.h"
  
***************
*** 34,46 ****
  struct hv_context hv_context = {
  	.synic_initialized	= false,
  	.hypercall_page		= NULL,
- 	.signal_event_param	= NULL,
- 	.signal_event_buffer	= NULL,
  };
  
  /*
   * query_hypervisor_info - Get version info of the windows hypervisor
   */
  static int query_hypervisor_info(void)
  {
  	unsigned int eax;
--- 35,50 ----
  struct hv_context hv_context = {
  	.synic_initialized	= false,
  	.hypercall_page		= NULL,
  };
  
  /*
   * query_hypervisor_info - Get version info of the windows hypervisor
   */
+ unsigned int host_info_eax;
+ unsigned int host_info_ebx;
+ unsigned int host_info_ecx;
+ unsigned int host_info_edx;
+ 
  static int query_hypervisor_info(void)
  {
  	unsigned int eax;
***************
*** 70,82 ****
  		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;
  }
--- 74,83 ----
  		edx = 0;
  		op = HVCPUID_VERSION;
  		cpuid(op, &eax, &ebx, &ecx, &edx);
! 		host_info_eax = eax;
! 		host_info_ebx = ebx;
! 		host_info_ecx = ecx;
! 		host_info_edx = edx;
  	}
  	return max_leaf;
  }
***************
*** 133,149 ****
  	int max_leaf;
  	union hv_x64_msr_hypercall_contents hypercall_msr;
  	void *virtaddr = NULL;
  
  	memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS);
  	memset(hv_context.synic_message_page, 0,
  	       sizeof(void *) * NR_CPUS);
  
  	max_leaf = query_hypervisor_info();
  
  	/*
  	 * Write our OS ID.
  	 */
! 	hv_context.guestid = generate_guest_id(0, LINUX_VERSION_CODE, 0);
  	wrmsrl(HV_X64_MSR_GUEST_OS_ID, hv_context.guestid);
  
  	/* See if the hypercall page is already set */
--- 134,156 ----
  	int max_leaf;
  	union hv_x64_msr_hypercall_contents hypercall_msr;
  	void *virtaddr = NULL;
+ 	__u8 d1 = 0x10; /* SuSE */
+ 	__u16 d2 = 0x0; /* -d of a.b.c-d */
  
  	memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS);
  	memset(hv_context.synic_message_page, 0,
  	       sizeof(void *) * NR_CPUS);
+ 	memset(hv_context.vp_index, 0,
+ 	       sizeof(int) * NR_CPUS);
+ 	memset(hv_context.event_dpc, 0,
+ 	       sizeof(void *) * NR_CPUS);
  
  	max_leaf = query_hypervisor_info();
  
  	/*
  	 * Write our OS ID.
  	 */
! 	hv_context.guestid = generate_guest_id(d1, LINUX_VERSION_CODE, d2);
  	wrmsrl(HV_X64_MSR_GUEST_OS_ID, hv_context.guestid);
  
  	/* See if the hypercall page is already set */
***************
*** 168,191 ****
  
  	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 0;
  
  cleanup:
--- 175,180 ----
***************
*** 213,222 ****
  	/* Reset our OS id */
  	wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);
  
- 	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);
--- 202,207 ----
***************
*** 273,285 ****
   *
   * 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;
  }
  
--- 258,269 ----
   *
   * This involves a hypercall.
   */
! u16 hv_signal_event(void *con_id)
  {
  	u16 status;
  
! 	status = (do_hypercall(HVCALL_SIGNAL_EVENT, con_id, NULL) & 0xFFFF);
! 
  	return status;
  }
  
***************
*** 290,304 ****
   * 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)
--- 274,288 ----
   * retrieve the initialized message and event pages.  Otherwise, we create and
   * initialize the message and event pages.
   */
! void hv_synic_init(void *arg)
  {
  	u64 version;
  	union hv_synic_simp simp;
  	union hv_synic_siefp siefp;
  	union hv_synic_sint shared_sint;
  	union hv_synic_scontrol sctrl;
+ 	u64 vp_index;
  
  	int cpu = smp_processor_id();
  
  	if (!hv_context.hypercall_page)
***************
*** 307,312 ****
--- 291,305 ----
  	/* Check the version */
  	rdmsrl(HV_X64_MSR_SVERSION, version);
  
+ 	hv_context.event_dpc[cpu] = (struct tasklet_struct *)
+ 					kmalloc(sizeof(struct tasklet_struct),
+ 						GFP_ATOMIC);
+ 	if (hv_context.event_dpc[cpu] == NULL) {
+ 		pr_err("Unable to allocate event dpc\n");
+ 		goto cleanup;
+ 	}
+ 	tasklet_init(hv_context.event_dpc[cpu], vmbus_on_event, cpu);
+ 
  	hv_context.synic_message_page[cpu] =
  		(void *)get_zeroed_page(GFP_ATOMIC);
  
***************
*** 343,351 ****
  	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 = false;
  
  	wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
  
--- 336,344 ----
  	rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
  
  	shared_sint.as_uint64 = 0;
! 	shared_sint.vector = HYPERVISOR_CALLBACK_VECTOR;
  	shared_sint.masked = false;
! 	shared_sint.auto_eoi = true;
  
  	wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
  
***************
*** 356,361 ****
--- 349,362 ----
  	wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64);
  
  	hv_context.synic_initialized = true;
+ 
+ 	/*
+ 	 * Setup the mapping between Hyper-V's notion
+ 	 * of cpuid and Linux' notion of cpuid.
+ 	 * This array will be indexed using Linux cpuid.
+ 	 */
+ 	rdmsrl(HV_X64_MSR_VP_INDEX, vp_index);
+ 	hv_context.vp_index[cpu] = (u32)vp_index;
  	return;
  
  cleanup:
diff -crN linux-3.8.6-gentoo/drivers/hv/hv_balloon.c linux-3.8.6-gentoo-hyperv/drivers/hv/hv_balloon.c
*** linux-3.8.6-gentoo/drivers/hv/hv_balloon.c	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/drivers/hv/hv_balloon.c	Sat Apr 13 02:24:31 2013
***************
*** 29,35 ****
  #include <linux/memory_hotplug.h>
  #include <linux/memory.h>
  #include <linux/notifier.h>
- #include <linux/mman.h>
  #include <linux/percpu_counter.h>
  
  #include <linux/hyperv.h>
--- 29,34 ----
***************
*** 118,124 ****
  	struct {
  		__u64 balloon:1;
  		__u64 hot_add:1;
! 		__u64 reservedz:62;
  	} cap_bits;
  	__u64 caps;
  } __packed;
--- 117,130 ----
  	struct {
  		__u64 balloon:1;
  		__u64 hot_add:1;
! 		/*
! 		 * To support guests that may have alignment
! 		 * limitations on hot-add, the guest can specify
! 		 * its alignment requirements; a value of n
! 		 * represents an alignment of 2^n in mega bytes.
! 		 */
! 		__u64 hot_add_alignment:4;
! 		__u64 reservedz:58;
  	} cap_bits;
  	__u64 caps;
  } __packed;
***************
*** 413,424 ****
   * End protocol definitions.
   */
  
! static bool hot_add;
! static bool do_hot_add;
  
  module_param(hot_add, bool, (S_IRUGO | S_IWUSR));
  MODULE_PARM_DESC(hot_add, "If set attempt memory hot_add");
  
  static atomic_t trans_id = ATOMIC_INIT(0);
  
  static int dm_ring_size = (5 * PAGE_SIZE);
--- 419,468 ----
   * End protocol definitions.
   */
  
! /*
!  * State to manage hot adding memory into the guest.
!  * The range start_pfn : end_pfn specifies the range
!  * that the host has asked us to hot add. The range
!  * start_pfn : ha_end_pfn specifies the range that we have
!  * currently hot added. We hot add in multiples of 128M
!  * chunks; it is possible that we may not be able to bring
!  * online all the pages in the region. The range
!  * covered_start_pfn : covered_end_pfn defines the pages that can
!  * be brough online.
!  */
! 
! struct hv_hotadd_state {
! 	struct list_head list;
! 	unsigned long start_pfn;
! 	unsigned long covered_start_pfn;
! 	unsigned long covered_end_pfn;
! 	unsigned long ha_end_pfn;
! 	unsigned long end_pfn;
! };
! 
! struct balloon_state {
! 	__u32 num_pages;
! 	struct work_struct wrk;
! };
! 
! struct hot_add_wrk {
! 	union dm_mem_page_range ha_page_range;
! 	union dm_mem_page_range ha_region_range;
! 	struct work_struct wrk;
! };
! 
! static bool hot_add = true;
! /*
!  * Delay reporting memory pressure by
!  * the specified number of seconds.
!  */
! static uint pressure_report_delay = 45;
  
  module_param(hot_add, bool, (S_IRUGO | S_IWUSR));
  MODULE_PARM_DESC(hot_add, "If set attempt memory hot_add");
  
+ module_param(pressure_report_delay, uint, (S_IRUGO | S_IWUSR));
+ MODULE_PARM_DESC(pressure_report_delay, "Delay in secs in reporting pressure");
  static atomic_t trans_id = ATOMIC_INIT(0);
  
  static int dm_ring_size = (5 * PAGE_SIZE);
***************
*** 440,445 ****
--- 484,490 ----
  static __u8 recv_buffer[PAGE_SIZE];
  static __u8 *send_buffer;
  #define PAGES_IN_2M	512
+ #define HA_CHUNK 512 
  
  struct hv_dynmem_device {
  	struct hv_device *dev;
***************
*** 453,459 ****
  	unsigned int num_pages_ballooned;
  
  	/*
! 	 * This thread handles both balloon/hot-add
  	 * requests from the host as well as notifying
  	 * the host with regards to memory pressure in
  	 * the guest.
--- 498,525 ----
  	unsigned int num_pages_ballooned;
  
  	/*
! 	 * State to manage the ballooning (up) operation.
! 	 */
! 	struct balloon_state balloon_wrk;
! 
! 	/*
! 	 * State to execute the "hot-add" operation.
! 	 */
! 	struct hot_add_wrk ha_wrk;
! 
! 	/*
! 	 * This state tracks if the host has specified a hot-add
! 	 * region.
! 	 */
! 	bool host_specified_ha_region;
! 
! 	/*
! 	 * State to synchronize hot-add.
! 	 */
! 	struct completion  ol_waitevent;
! 	bool ha_waiting;
! 	/*
! 	 * This thread handles hot-add
  	 * requests from the host as well as notifying
  	 * the host with regards to memory pressure in
  	 * the guest.
***************
*** 461,466 ****
--- 527,537 ----
  	struct task_struct *thread;
  
  	/*
+ 	 * A list of hot-add regions.
+ 	 */
+ 	struct list_head ha_region_list;
+ 
+ 	/*
  	 * We start with the highest version we can support
  	 * and downgrade based on the host; we save here the
  	 * next version to try.
***************
*** 470,504 ****
  
  static struct hv_dynmem_device dm_device;
  
! static void hot_add_req(struct hv_dynmem_device *dm, struct dm_hot_add *msg)
  {
  
! 	struct dm_hot_add_response resp;
  
! 	if (do_hot_add) {
  
! 		pr_info("Memory hot add not supported\n");
  
  		/*
! 		 * Currently we do not support hot add.
! 		 * Just fail the request.
  		 */
  	}
  
  	memset(&resp, 0, sizeof(struct dm_hot_add_response));
  	resp.hdr.type = DM_MEM_HOT_ADD_RESPONSE;
  	resp.hdr.size = sizeof(struct dm_hot_add_response);
  	resp.hdr.trans_id = atomic_inc_return(&trans_id);
  
! 	resp.page_count = 0;
! 	resp.result = 0;
  
  	dm->state = DM_INITIALIZED;
  	vmbus_sendpacket(dm->dev->channel, &resp,
  			sizeof(struct dm_hot_add_response),
  			(unsigned long)NULL,
  			VM_PKT_DATA_INBAND, 0);
- 
  }
  
  static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg)
--- 541,868 ----
  
  static struct hv_dynmem_device dm_device;
  
! #ifdef CONFIG_MEMORY_HOTPLUG
! 
! void hv_bring_pgs_online(unsigned long start_pfn, unsigned long size)
  {
+ 	int i;
  
! 	for (i = 0; i < size; i++) {
! 		struct page *pg;
! 		pg = pfn_to_page(start_pfn + i);
! 		__online_page_set_limits(pg);
! 		__online_page_increment_counters(pg);
! 		__online_page_free(pg);
! 	}
! }
! 
! static void hv_mem_hot_add(unsigned long start, unsigned long size,
! 				unsigned long pfn_count,
! 				struct hv_hotadd_state *has)
! {
! 	int ret = 0;
! 	int i, nid, t;
! 	unsigned long start_pfn;
! 	unsigned long processed_pfn;
! 	unsigned long total_pfn = pfn_count;
! 
! 	for (i = 0; i < (size/HA_CHUNK); i++) {
! 		start_pfn = start + (i * HA_CHUNK);
! 		has->ha_end_pfn +=  HA_CHUNK;
! 
! 		if (total_pfn > HA_CHUNK) {
! 			processed_pfn = HA_CHUNK;
! 			total_pfn -= HA_CHUNK;
! 		} else {
! 			processed_pfn = total_pfn;
! 			total_pfn = 0;
! 		}
! 
! 		has->covered_end_pfn +=  processed_pfn;
! 
! 		init_completion(&dm_device.ol_waitevent);
! 		dm_device.ha_waiting = true;
! 
! 		nid = memory_add_physaddr_to_nid(PFN_PHYS(start_pfn));
! 		ret = add_memory(nid, PFN_PHYS((start_pfn)),
! 				(HA_CHUNK << PAGE_SHIFT));
! 
! 		if (ret) {
! 			pr_info("hot_add memory failed error is %d\n", ret);
! 			has->ha_end_pfn -= HA_CHUNK;
! 			has->covered_end_pfn -=  processed_pfn;
! 			break;
! 		}
! 
! 		/*
! 		 * Wait for the memory block to be onlined.
! 		 */
! 		t = wait_for_completion_timeout(&dm_device.ol_waitevent, 10*HZ);
! 		if (t == 0) {
! 			pr_info("hot_add memory timedout\n");
! 			has->ha_end_pfn -= HA_CHUNK;
! 			has->covered_end_pfn -=  processed_pfn;
! 			break;
! 		}
! 	}
! 
! 	return;
! }
! 
! static void hv_online_page(struct page *pg)
! {
! 	struct list_head *cur;
! 	struct hv_hotadd_state *has;
! 	unsigned long cur_start_pgp;
! 	unsigned long cur_end_pgp;
! 
! 	if (dm_device.ha_waiting) {
! 		dm_device.ha_waiting = false;
! 		complete(&dm_device.ol_waitevent);
! 	}
! 
! 	list_for_each(cur, &dm_device.ha_region_list) {
! 		has = list_entry(cur, struct hv_hotadd_state, list);
! 		cur_start_pgp = (unsigned long)
! 				pfn_to_page(has->covered_start_pfn);
! 		cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn);
! 
! 		if (((unsigned long)pg >= cur_start_pgp) &&
! 			((unsigned long)pg < cur_end_pgp)) {
! 			/*
! 			 * This frame is currently backed; online the
! 			 * page.
! 			 */
! 			__online_page_set_limits(pg);
! 			__online_page_increment_counters(pg);
! 			__online_page_free(pg);
! 			has->covered_start_pfn++;
! 		}
! 	}
! }
! 
! static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt)
! {
! 	struct list_head *cur;
! 	struct hv_hotadd_state *has;
! 	unsigned long residual, new_inc;
! 
! 	if (list_empty(&dm_device.ha_region_list))
! 		return false;
  
! 	list_for_each(cur, &dm_device.ha_region_list) {
! 		has = list_entry(cur, struct hv_hotadd_state, list);
  
! 		/*
! 		 * If the pfn range we are dealing with is not in the current
! 		 * "hot add block", move on.
! 		 */
! 		if ((start_pfn >= has->end_pfn))
! 			continue;
! 		/*
! 		 * If the current hot add-request extends beyond
! 		 * our current limit; extend it.
! 		 */
! 		if ((start_pfn + pfn_cnt) > has->end_pfn) {
! 			residual = (start_pfn + pfn_cnt - has->end_pfn);
! 			/*
! 			 * Extend the region by multiples of HA_CHUNK.
! 			 */
! 			new_inc = (residual / HA_CHUNK) * HA_CHUNK;
! 			if (residual % HA_CHUNK)
! 				new_inc += HA_CHUNK;
! 
! 			has->end_pfn += new_inc;
! 		}
  
  		/*
! 		 * If the current start pfn is not where the covered_end
! 		 * is, update it.
  		 */
+ 
+ 		if (has->covered_end_pfn != start_pfn) {
+ 			has->covered_end_pfn = start_pfn;
+ 			has->covered_start_pfn = start_pfn;
+ 		}
+ 		return true;
+ 
  	}
  
+ 	return false;
+ }
+ 
+ static unsigned long handle_pg_range(unsigned long pg_start,
+ 					unsigned long pg_count)
+ {
+ 	unsigned long start_pfn = pg_start;
+ 	unsigned long pfn_cnt = pg_count;
+ 	unsigned long size;
+ 	struct list_head *cur;
+ 	struct hv_hotadd_state *has;
+ 	unsigned long pgs_ol = 0;
+ 	unsigned long old_covered_state;
+ 
+ 	if (list_empty(&dm_device.ha_region_list))
+ 		return 0;
+ 
+ 	list_for_each(cur, &dm_device.ha_region_list) {
+ 		has = list_entry(cur, struct hv_hotadd_state, list);
+ 
+ 		/*
+ 		 * If the pfn range we are dealing with is not in the current
+ 		 * "hot add block", move on.
+ 		 */
+ 		if ((start_pfn >= has->end_pfn))
+ 			continue;
+ 
+ 		old_covered_state = has->covered_end_pfn;
+ 
+ 		if (start_pfn < has->ha_end_pfn) {
+ 			/*
+ 			 * This is the case where we are backing pages
+ 			 * in an already hot added region. Bring
+ 			 * these pages online first.
+ 			 */
+ 			pgs_ol = has->ha_end_pfn - start_pfn;
+ 			if (pgs_ol > pfn_cnt)
+ 				pgs_ol = pfn_cnt;
+ 			hv_bring_pgs_online(start_pfn, pgs_ol);
+ 			has->covered_end_pfn +=  pgs_ol;
+ 			has->covered_start_pfn +=  pgs_ol;
+ 			pfn_cnt -= pgs_ol;
+ 		}
+ 
+ 		if ((has->ha_end_pfn < has->end_pfn) && (pfn_cnt > 0)) {
+ 			/*
+ 			 * We have some residual hot add range
+ 			 * that needs to be hot added; hot add
+ 			 * it now. Hot add a multiple of
+ 			 * of HA_CHUNK that fully covers the pages
+ 			 * we have.
+ 			 */
+ 			size = (has->end_pfn - has->ha_end_pfn);
+ 			if (pfn_cnt <= size) {
+ 				size = ((pfn_cnt / HA_CHUNK) * HA_CHUNK);
+ 				if (pfn_cnt % HA_CHUNK)
+ 					size += HA_CHUNK;
+ 			} else {
+ 				pfn_cnt = size;
+ 			}
+ 			hv_mem_hot_add(has->ha_end_pfn, size, pfn_cnt, has);
+ 		}
+ 		/*
+ 		 * If we managed to online any pages that were given to us,
+ 		 * we declare success.
+ 		 */
+ 		return has->covered_end_pfn - old_covered_state;
+ 
+ 	}
+ 
+ 	return 0;
+ }
+ 
+ static unsigned long process_hot_add(unsigned long pg_start,
+ 					unsigned long pfn_cnt,
+ 					unsigned long rg_start,
+ 					unsigned long rg_size)
+ {
+ 	struct hv_hotadd_state *ha_region = NULL;
+ 
+ 	if (pfn_cnt == 0)
+ 		return 0;
+ 
+ 	if (!dm_device.host_specified_ha_region)
+ 		if (pfn_covered(pg_start, pfn_cnt))
+ 			goto do_pg_range;
+ 
+ 	/*
+ 	 * If the host has specified a hot-add range; deal with it first.
+ 	 */
+ 
+ 	if (rg_size != 0) {
+ 		ha_region = kzalloc(sizeof(struct hv_hotadd_state), GFP_KERNEL);
+ 		if (!ha_region)
+ 			return 0;
+ 
+ 		INIT_LIST_HEAD(&ha_region->list);
+ 
+ 		list_add_tail(&ha_region->list, &dm_device.ha_region_list);
+ 		ha_region->start_pfn = rg_start;
+ 		ha_region->ha_end_pfn = rg_start;
+ 		ha_region->covered_start_pfn = pg_start;
+ 		ha_region->covered_end_pfn = pg_start;
+ 		ha_region->end_pfn = rg_start + rg_size;
+ 	}
+ 
+ do_pg_range:
+ 	/*
+ 	 * Process the page range specified; bringing them
+ 	 * online if possible.
+ 	 */
+ 	return handle_pg_range(pg_start, pfn_cnt);
+ }
+ 
+ #endif
+ 
+ static void hot_add_req(struct work_struct *dummy)
+ {
+ 	struct dm_hot_add_response resp;
+ #ifdef CONFIG_MEMORY_HOTPLUG
+ 	unsigned long pg_start, pfn_cnt;
+ 	unsigned long rg_start, rg_sz;
+ #endif
+ 	struct hv_dynmem_device *dm = &dm_device;
+ 
  	memset(&resp, 0, sizeof(struct dm_hot_add_response));
  	resp.hdr.type = DM_MEM_HOT_ADD_RESPONSE;
  	resp.hdr.size = sizeof(struct dm_hot_add_response);
  	resp.hdr.trans_id = atomic_inc_return(&trans_id);
  
! #ifdef CONFIG_MEMORY_HOTPLUG
! 	pg_start = dm->ha_wrk.ha_page_range.finfo.start_page;
! 	pfn_cnt = dm->ha_wrk.ha_page_range.finfo.page_cnt;
! 
! 	rg_start = dm->ha_wrk.ha_region_range.finfo.start_page;
! 	rg_sz = dm->ha_wrk.ha_region_range.finfo.page_cnt;
! 
! 	if ((rg_start == 0) && (!dm->host_specified_ha_region)) {
! 		unsigned long region_size;
! 		unsigned long region_start;
! 
! 		/*
! 		 * The host has not specified the hot-add region.
! 		 * Based on the hot-add page range being specified,
! 		 * compute a hot-add region that can cover the pages
! 		 * that need to be hot-added while ensuring the alignment
! 		 * and size requirements of Linux as it relates to hot-add.
! 		 */
! 		region_start = pg_start;
! 		region_size = (pfn_cnt / HA_CHUNK) * HA_CHUNK;
! 		if (pfn_cnt % HA_CHUNK)
! 			region_size += HA_CHUNK;
! 
! 		region_start = (pg_start / HA_CHUNK) * HA_CHUNK;
! 
! 		rg_start = region_start;
! 		rg_sz = region_size;
! 	}
! 
! 	resp.page_count = process_hot_add(pg_start, pfn_cnt,
! 					rg_start, rg_sz);
! #endif
! 	if (resp.page_count > 0)
! 		resp.result = 1;
! 	else
! 		resp.result = 0;
! 
! 	if (resp.page_count == 0)
! 		pr_info("Memory hot add failed\n");
  
  	dm->state = DM_INITIALIZED;
  	vmbus_sendpacket(dm->dev->channel, &resp,
  			sizeof(struct dm_hot_add_response),
  			(unsigned long)NULL,
  			VM_PKT_DATA_INBAND, 0);
  }
  
  static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg)
***************
*** 517,522 ****
--- 881,914 ----
  	}
  }
  
+ unsigned long compute_balloon_floor(void)
+ {
+ 	unsigned long min_pages;
+ #define MB2PAGES(mb) ((mb) << (20 - PAGE_SHIFT))
+ 	/* Simple continuous piecewiese linear function:
+ 	 *  max MiB -> min MiB  gradient
+ 	 *       0         0
+ 	 *      16        16
+ 	 *      32        24
+ 	 *     128        72    (1/2)
+ 	 *     512       168    (1/4)
+ 	 *    2048       360    (1/8)
+ 	 *    8192       552    (1/32)
+ 	 *   32768      1320
+ 	 *  131072      4392
+ 	 */
+ 	if (totalram_pages < MB2PAGES(128))
+ 		min_pages = MB2PAGES(8) + (totalram_pages >> 1);
+ 	else if (totalram_pages < MB2PAGES(512))
+ 		min_pages = MB2PAGES(40) + (totalram_pages >> 2);
+ 	else if (totalram_pages < MB2PAGES(2048))
+ 		min_pages = MB2PAGES(104) + (totalram_pages >> 3);
+ 	else
+ 		min_pages = MB2PAGES(296) + (totalram_pages >> 5);
+ #undef MB2PAGES
+ 	return min_pages;
+ }
+ 
  /*
   * Post our status as it relates memory pressure to the
   * host. Host expects the guests to post this status
***************
*** 530,544 ****
  static void post_status(struct hv_dynmem_device *dm)
  {
  	struct dm_status status;
  
! 
  	memset(&status, 0, sizeof(struct dm_status));
  	status.hdr.type = DM_STATUS_REPORT;
  	status.hdr.size = sizeof(struct dm_status);
  	status.hdr.trans_id = atomic_inc_return(&trans_id);
  
! 
! 	status.num_committed = vm_memory_committed();
  
  	vmbus_sendpacket(dm->dev->channel, &status,
  				sizeof(struct dm_status),
--- 922,951 ----
  static void post_status(struct hv_dynmem_device *dm)
  {
  	struct dm_status status;
+ 	struct sysinfo val;
  
! 	if (pressure_report_delay > 0) {
! 		--pressure_report_delay;
! 		return;
! 	}
! 	si_meminfo(&val);
  	memset(&status, 0, sizeof(struct dm_status));
  	status.hdr.type = DM_STATUS_REPORT;
  	status.hdr.size = sizeof(struct dm_status);
  	status.hdr.trans_id = atomic_inc_return(&trans_id);
  
! 	/*
! 	 * The host expects the guest to report free memory.
! 	 * Further, the host expects the pressure information to
! 	 * include the ballooned out pages.
! 	 * For a given amount of memory that we are managing, we
! 	 * need to compute a floor below which we should not balloon.
! 	 * Compute this and add it to the pressure report.
! 	 */
! 	status.num_avail = val.freeram;
! 	status.num_committed = vm_memory_committed() +
! 				dm->num_pages_ballooned +
! 				compute_balloon_floor();
  
  	vmbus_sendpacket(dm->dev->channel, &status,
  				sizeof(struct dm_status),
***************
*** 547,554 ****
  
  }
  
- 
- 
  static void free_balloon_pages(struct hv_dynmem_device *dm,
  			 union dm_mem_page_range *range_array)
  {
--- 954,959 ----
***************
*** 597,602 ****
--- 1002,1015 ----
  
  		dm->num_pages_ballooned += alloc_unit;
  
+ 		/*
+ 		 * If we allocatted 2M pages; split them so we
+ 		 * can free them in any order we get.
+ 		 */
+ 
+ 		if (alloc_unit != 1)
+ 			split_page(pg, get_order(alloc_unit << PAGE_SHIFT));
+ 
  		bl_resp->range_count++;
  		bl_resp->range_array[i].finfo.start_page =
  			page_to_pfn(pg);
***************
*** 610,618 ****
  
  
  
! static void balloon_up(struct hv_dynmem_device *dm, struct dm_balloon *req)
  {
! 	int num_pages = req->num_pages;
  	int num_ballooned = 0;
  	struct dm_balloon_response *bl_resp;
  	int alloc_unit;
--- 1023,1031 ----
  
  
  
! static void balloon_up(struct work_struct *dummy)
  {
! 	int num_pages = dm_device.balloon_wrk.num_pages;
  	int num_ballooned = 0;
  	struct dm_balloon_response *bl_resp;
  	int alloc_unit;
***************
*** 623,631 ****
  
  
  	/*
! 	 * Currently, we only support 4k allocations.
  	 */
! 	alloc_unit = 1;
  
  	while (!done) {
  		bl_resp = (struct dm_balloon_response *)send_buffer;
--- 1036,1045 ----
  
  
  	/*
! 	 * We will attempt 2M allocations. However, if we fail to
! 	 * allocate 2M chunks, we will go back to 4k allocations.
  	 */
! 	alloc_unit = 512;
  
  	while (!done) {
  		bl_resp = (struct dm_balloon_response *)send_buffer;
***************
*** 637,650 ****
  
  
  		num_pages -= num_ballooned;
! 		num_ballooned = alloc_balloon_pages(dm, num_pages,
  						bl_resp, alloc_unit,
  						 &alloc_error);
  
  		if ((alloc_error) || (num_ballooned == num_pages)) {
  			bl_resp->more_pages = 0;
  			done = true;
! 			dm->state = DM_INITIALIZED;
  		}
  
  		/*
--- 1051,1069 ----
  
  
  		num_pages -= num_ballooned;
! 		num_ballooned = alloc_balloon_pages(&dm_device, num_pages,
  						bl_resp, alloc_unit,
  						 &alloc_error);
  
+ 		if ((alloc_error) && (alloc_unit != 1)) {
+ 			alloc_unit = 1;
+ 			continue;
+ 		}
+ 
  		if ((alloc_error) || (num_ballooned == num_pages)) {
  			bl_resp->more_pages = 0;
  			done = true;
! 			dm_device.state = DM_INITIALIZED;
  		}
  
  		/*
***************
*** 672,678 ****
  			pr_info("Balloon response failed\n");
  
  			for (i = 0; i < bl_resp->range_count; i++)
! 				free_balloon_pages(dm,
  						 &bl_resp->range_array[i]);
  
  			done = true;
--- 1091,1097 ----
  			pr_info("Balloon response failed\n");
  
  			for (i = 0; i < bl_resp->range_count; i++)
! 				free_balloon_pages(&dm_device,
  						 &bl_resp->range_array[i]);
  
  			done = true;
***************
*** 714,720 ****
  {
  	struct hv_dynmem_device *dm = dm_dev;
  	int t;
- 	unsigned long  scan_start;
  
  	while (!kthread_should_stop()) {
  		t = wait_for_completion_timeout(&dm_device.config_event, 1*HZ);
--- 1133,1138 ----
***************
*** 726,747 ****
  		if (t == 0)
  			post_status(dm);
  
- 		scan_start = jiffies;
- 		switch (dm->state) {
- 		case DM_BALLOON_UP:
- 			balloon_up(dm, (struct dm_balloon *)recv_buffer);
- 			break;
- 
- 		case DM_HOT_ADD:
- 			hot_add_req(dm, (struct dm_hot_add *)recv_buffer);
- 			break;
- 		default:
- 			break;
- 		}
- 
- 		if (!time_in_range(jiffies, scan_start, scan_start + HZ))
- 			post_status(dm);
- 
  	}
  
  	return 0;
--- 1144,1149 ----
***************
*** 814,819 ****
--- 1216,1225 ----
  	struct dm_message *dm_msg;
  	struct dm_header *dm_hdr;
  	struct hv_dynmem_device *dm = hv_get_drvdata(dev);
+ 	struct dm_balloon *bal_msg;
+ 	struct dm_hot_add *ha_msg;
+ 	union dm_mem_page_range *ha_pg_range;
+ 	union dm_mem_page_range *ha_region;
  
  	memset(recv_buffer, 0, sizeof(recv_buffer));
  	vmbus_recvpacket(dev->channel, recv_buffer,
***************
*** 835,842 ****
  			break;
  
  		case DM_BALLOON_REQUEST:
  			dm->state = DM_BALLOON_UP;
! 			complete(&dm->config_event);
  			break;
  
  		case DM_UNBALLOON_REQUEST:
--- 1241,1252 ----
  			break;
  
  		case DM_BALLOON_REQUEST:
+ 			if (dm->state == DM_BALLOON_UP)
+ 				pr_warn("Currently ballooning\n");
+ 			bal_msg = (struct dm_balloon *)recv_buffer;
  			dm->state = DM_BALLOON_UP;
! 			dm_device.balloon_wrk.num_pages = bal_msg->num_pages;
! 			schedule_work(&dm_device.balloon_wrk.wrk);
  			break;
  
  		case DM_UNBALLOON_REQUEST:
***************
*** 846,853 ****
  			break;
  
  		case DM_MEM_HOT_ADD_REQUEST:
  			dm->state = DM_HOT_ADD;
! 			complete(&dm->config_event);
  			break;
  
  		case DM_INFO_MESSAGE:
--- 1256,1287 ----
  			break;
  
  		case DM_MEM_HOT_ADD_REQUEST:
+ 			if (dm->state == DM_HOT_ADD)
+ 				pr_warn("Currently hot-adding\n");
  			dm->state = DM_HOT_ADD;
! 			ha_msg = (struct dm_hot_add *)recv_buffer;
! 			if (ha_msg->hdr.size == sizeof(struct dm_hot_add)) {
! 				/*
! 				 * This is a normal hot-add request specifying
! 				 * hot-add memory.
! 				 */
! 				dm->host_specified_ha_region = false;
! 				ha_pg_range = &ha_msg->range;
! 				dm->ha_wrk.ha_page_range = *ha_pg_range;
! 				dm->ha_wrk.ha_region_range.page_range = 0;
! 			} else {
! 				/*
! 				 * Host is specifying that we first hot-add
! 				 * a region and then partially populate this
! 				 * region.
! 				 */
! 				dm->host_specified_ha_region = true;
! 				ha_pg_range = &ha_msg->range;
! 				ha_region = &ha_pg_range[1];
! 				dm->ha_wrk.ha_page_range = *ha_pg_range;
! 				dm->ha_wrk.ha_region_range = *ha_region;
! 			}
! 			schedule_work(&dm_device.ha_wrk.wrk);
  			break;
  
  		case DM_INFO_MESSAGE:
***************
*** 869,876 ****
  	struct dm_version_request version_req;
  	struct dm_capabilities cap_msg;
  
- 	do_hot_add = hot_add;
- 
  	/*
  	 * First allocate a send buffer.
  	 */
--- 1303,1308 ----
***************
*** 890,895 ****
--- 1322,1331 ----
  	dm_device.next_version = DYNMEM_PROTOCOL_VERSION_WIN7;
  	init_completion(&dm_device.host_event);
  	init_completion(&dm_device.config_event);
+ 	INIT_LIST_HEAD(&dm_device.ha_region_list);
+ 	INIT_WORK(&dm_device.balloon_wrk.wrk, balloon_up);
+ 	INIT_WORK(&dm_device.ha_wrk.wrk, hot_add_req);
+ 	dm_device.host_specified_ha_region = false;
  
  	dm_device.thread =
  		 kthread_run(dm_thread_func, &dm_device, "hv_balloon");
***************
*** 898,903 ****
--- 1334,1343 ----
  		goto probe_error1;
  	}
  
+ #ifdef CONFIG_MEMORY_HOTPLUG
+ 	set_online_page_callback(&hv_online_page);
+ #endif
+ 
  	hv_set_drvdata(dev, &dm_device);
  	/*
  	 * Initiate the hand shake with the host and negotiate
***************
*** 915,922 ****
  	ret = vmbus_sendpacket(dev->channel, &version_req,
  				sizeof(struct dm_version_request),
  				(unsigned long)NULL,
! 				VM_PKT_DATA_INBAND,
! 				VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
  	if (ret)
  		goto probe_error2;
  
--- 1355,1361 ----
  	ret = vmbus_sendpacket(dev->channel, &version_req,
  				sizeof(struct dm_version_request),
  				(unsigned long)NULL,
! 				VM_PKT_DATA_INBAND, 0);
  	if (ret)
  		goto probe_error2;
  
***************
*** 943,955 ****
  	cap_msg.hdr.trans_id = atomic_inc_return(&trans_id);
  
  	cap_msg.caps.cap_bits.balloon = 1;
  	/*
! 	 * While we currently don't support hot-add,
! 	 * we still advertise this capability since the
! 	 * host requires that guests partcipating in the
! 	 * dynamic memory protocol support hot add.
  	 */
! 	cap_msg.caps.cap_bits.hot_add = 1;
  
  	/*
  	 * Currently the host does not use these
--- 1382,1394 ----
  	cap_msg.hdr.trans_id = atomic_inc_return(&trans_id);
  
  	cap_msg.caps.cap_bits.balloon = 1;
+ 	cap_msg.caps.cap_bits.hot_add = 1;
+ 
  	/*
! 	 * Specify our alignment requirements as it relates
! 	 * memory hot-add. Specify 128MB alignment.
  	 */
! 	cap_msg.caps.cap_bits.hot_add_alignment = 7;
  
  	/*
  	 * Currently the host does not use these
***************
*** 962,969 ****
  	ret = vmbus_sendpacket(dev->channel, &cap_msg,
  				sizeof(struct dm_capabilities),
  				(unsigned long)NULL,
! 				VM_PKT_DATA_INBAND,
! 				VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
  	if (ret)
  		goto probe_error2;
  
--- 1401,1407 ----
  	ret = vmbus_sendpacket(dev->channel, &cap_msg,
  				sizeof(struct dm_capabilities),
  				(unsigned long)NULL,
! 				VM_PKT_DATA_INBAND, 0);
  	if (ret)
  		goto probe_error2;
  
***************
*** 987,992 ****
--- 1425,1433 ----
  	return 0;
  
  probe_error2:
+ #ifdef CONFIG_MEMORY_HOTPLUG
+ 	restore_online_page_callback(&hv_online_page);
+ #endif
  	kthread_stop(dm_device.thread);
  
  probe_error1:
***************
*** 999,1011 ****
--- 1440,1465 ----
  static int balloon_remove(struct hv_device *dev)
  {
  	struct hv_dynmem_device *dm = hv_get_drvdata(dev);
+ 	struct list_head *cur, *tmp;
+ 	struct hv_hotadd_state *has;
  
  	if (dm->num_pages_ballooned != 0)
  		pr_warn("Ballooned pages: %d\n", dm->num_pages_ballooned);
  
+ 	cancel_work_sync(&dm->balloon_wrk.wrk);
+ 	cancel_work_sync(&dm->ha_wrk.wrk);
+ 
  	vmbus_close(dev->channel);
  	kthread_stop(dm->thread);
  	kfree(send_buffer);
+ #ifdef CONFIG_MEMORY_HOTPLUG
+ 	restore_online_page_callback(&hv_online_page);
+ #endif
+ 	list_for_each_safe(cur, tmp, &dm->ha_region_list) {
+ 		has = list_entry(cur, struct hv_hotadd_state, list);
+ 		list_del(&has->list);
+ 		kfree(has);
+ 	}
  
  	return 0;
  }
***************
*** 1013,1021 ****
  static const struct hv_vmbus_device_id id_table[] = {
  	/* Dynamic Memory Class ID */
  	/* 525074DC-8985-46e2-8057-A307DC18A502 */
! 	{ VMBUS_DEVICE(0xdc, 0x74, 0x50, 0X52, 0x85, 0x89, 0xe2, 0x46,
! 		       0x80, 0x57, 0xa3, 0x07, 0xdc, 0x18, 0xa5, 0x02)
! 	},
  	{ },
  };
  
--- 1467,1473 ----
  static const struct hv_vmbus_device_id id_table[] = {
  	/* Dynamic Memory Class ID */
  	/* 525074DC-8985-46e2-8057-A307DC18A502 */
! 	{ HV_DM_GUID, },
  	{ },
  };
  
***************
*** 1034,1047 ****
  	return vmbus_driver_register(&balloon_drv);
  }
  
- static void exit_balloon_drv(void)
- {
- 
- 	vmbus_driver_unregister(&balloon_drv);
- }
- 
  module_init(init_balloon_drv);
- module_exit(exit_balloon_drv);
  
  MODULE_DESCRIPTION("Hyper-V Balloon");
  MODULE_VERSION(HV_DRV_VERSION);
--- 1486,1492 ----
diff -crN linux-3.8.6-gentoo/drivers/hv/hv_kvp.c linux-3.8.6-gentoo-hyperv/drivers/hv/hv_kvp.c
*** linux-3.8.6-gentoo/drivers/hv/hv_kvp.c	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/drivers/hv/hv_kvp.c	Sat Apr 13 02:24:31 2013
***************
*** 528,541 ****
  	 */
  	keylen = utf8s_to_utf16s(key_name, strlen(key_name), UTF16_HOST_ENDIAN,
  				(wchar_t *) kvp_data->key,
! 				(HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2) - 2);
  	kvp_data->key_size = 2*(keylen + 1); /* utf16 encoding */
  
  copy_value:
  	value = msg_to_host->body.kvp_enum_data.data.value;
  	valuelen = utf8s_to_utf16s(value, strlen(value), UTF16_HOST_ENDIAN,
  				(wchar_t *) kvp_data->value,
! 				(HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2) - 2);
  	kvp_data->value_size = 2*(valuelen + 1); /* utf16 encoding */
  
  	/*
--- 528,541 ----
  	 */
  	keylen = utf8s_to_utf16s(key_name, strlen(key_name), UTF16_HOST_ENDIAN,
  				(wchar_t *) kvp_data->key,
! 				HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2);
  	kvp_data->key_size = 2*(keylen + 1); /* utf16 encoding */
  
  copy_value:
  	value = msg_to_host->body.kvp_enum_data.data.value;
  	valuelen = utf8s_to_utf16s(value, strlen(value), UTF16_HOST_ENDIAN,
  				(wchar_t *) kvp_data->value,
! 				HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2);
  	kvp_data->value_size = 2*(valuelen + 1); /* utf16 encoding */
  
  	/*
diff -crN linux-3.8.6-gentoo/drivers/hv/hv_snapshot.c linux-3.8.6-gentoo-hyperv/drivers/hv/hv_snapshot.c
*** linux-3.8.6-gentoo/drivers/hv/hv_snapshot.c	Thu Jan  1 09:00:00 1970
--- linux-3.8.6-gentoo-hyperv/drivers/hv/hv_snapshot.c	Sat Apr 13 02:24:31 2013
***************
*** 0 ****
--- 1,287 ----
+ /*
+  * An implementation of host initiated guest snapshot.
+  *
+  *
+  * Copyright (C) 2013, Microsoft, Inc.
+  * Author : K. Y. Srinivasan <kys@microsoft.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.
+  *
+  */
+ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+ 
+ #include <linux/net.h>
+ #include <linux/nls.h>
+ #include <linux/connector.h>
+ #include <linux/workqueue.h>
+ #include <linux/hyperv.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. */
+ 	struct hv_vss_msg  *msg; /* current message */
+ } vss_transaction;
+ 
+ 
+ static void vss_respond_to_host(int error);
+ 
+ static struct cb_id vss_id = { CN_VSS_IDX, CN_VSS_VAL };
+ static const char vss_name[] = "vss_kernel_module";
+ static __u8 *recv_buffer;
+ 
+ static void vss_send_op(struct work_struct *dummy);
+ static DECLARE_WORK(vss_send_op_work, vss_send_op);
+ 
+ /*
+  * Callback when data is received from user mode.
+  */
+ 
+ static void
+ vss_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
+ {
+ 	struct hv_vss_msg *vss_msg;
+ 
+ 	vss_msg = (struct hv_vss_msg *)msg->data;
+ 
+ 	if (vss_msg->vss_hdr.operation == VSS_OP_REGISTER) {
+ 		pr_info("VSS daemon registered\n");
+ 		vss_transaction.active = false;
+ 		if (vss_transaction.recv_channel != NULL)
+ 			hv_vss_onchannelcallback(vss_transaction.recv_channel);
+ 		return;
+ 
+ 	}
+ 	vss_respond_to_host(vss_msg->error);
+ }
+ 
+ 
+ static void vss_send_op(struct work_struct *dummy)
+ {
+ 	int op = vss_transaction.msg->vss_hdr.operation;
+ 	struct cn_msg *msg;
+ 	struct hv_vss_msg *vss_msg;
+ 
+ 	msg = kzalloc(sizeof(*msg) + sizeof(*vss_msg), GFP_ATOMIC);
+ 	if (!msg)
+ 		return;
+ 
+ 	vss_msg = (struct hv_vss_msg *)msg->data;
+ 
+ 	msg->id.idx =  CN_VSS_IDX;
+ 	msg->id.val = CN_VSS_VAL;
+ 
+ 	vss_msg->vss_hdr.operation = op;
+ 	msg->len = sizeof(struct hv_vss_msg);
+ 
+ 	cn_netlink_send(msg, 0, GFP_ATOMIC);
+ 	kfree(msg);
+ 
+ 	return;
+ }
+ 
+ /*
+  * Send a response back to the host.
+  */
+ 
+ static void
+ vss_respond_to_host(int error)
+ {
+ 	struct icmsg_hdr *icmsghdrp;
+ 	u32	buf_len;
+ 	struct vmbus_channel *channel;
+ 	u64	req_id;
+ 
+ 	/*
+ 	 * If a transaction is not active; log and return.
+ 	 */
+ 
+ 	if (!vss_transaction.active) {
+ 		/*
+ 		 * This is a spurious call!
+ 		 */
+ 		pr_warn("VSS: 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 = vss_transaction.recv_len;
+ 	channel = vss_transaction.recv_channel;
+ 	req_id = vss_transaction.recv_req_id;
+ 	vss_transaction.active = false;
+ 
+ 	icmsghdrp = (struct icmsg_hdr *)
+ 			&recv_buffer[sizeof(struct vmbuspipe_hdr)];
+ 
+ 	if (channel->onchannel_callback == NULL)
+ 		/*
+ 		 * We have raced with util driver being unloaded;
+ 		 * silently return.
+ 		 */
+ 		return;
+ 
+ 	icmsghdrp->status = error;
+ 
+ 	icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
+ 
+ 	vmbus_sendpacket(channel, recv_buffer, buf_len, req_id,
+ 				VM_PKT_DATA_INBAND, 0);
+ 
+ }
+ 
+ /*
+  * This callback is invoked when we get a VSS message from the host.
+  * The host ensures that only one VSS transaction can be active at a time.
+  */
+ 
+ void hv_vss_onchannelcallback(void *context)
+ {
+ 	struct vmbus_channel *channel = context;
+ 	u32 recvlen;
+ 	u64 requestid;
+ 	struct hv_vss_msg *vss_msg;
+ 
+ 
+ 	struct icmsg_hdr *icmsghdrp;
+ 	struct icmsg_negotiate *negop = NULL;
+ 
+ 	if (vss_transaction.active) {
+ 		/*
+ 		 * We will defer processing this callback once
+ 		 * the current transaction is complete.
+ 		 */
+ 		vss_transaction.recv_channel = channel;
+ 		return;
+ 	}
+ 
+ 	vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen,
+ 			 &requestid);
+ 
+ 	if (recvlen > 0) {
+ 		icmsghdrp = (struct icmsg_hdr *)&recv_buffer[
+ 			sizeof(struct vmbuspipe_hdr)];
+ 
+ 		if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
+ 			vmbus_prep_negotiate_resp(icmsghdrp, negop,
+ 				 recv_buffer, MAX_SRV_VER, MAX_SRV_VER);
+ 			/*
+ 			 * We currently negotiate the highest number the
+ 			 * host has presented. If this version is not
+ 			 * atleast 5.0, reject.
+ 			 */
+ 			negop = (struct icmsg_negotiate *)&recv_buffer[
+ 				sizeof(struct vmbuspipe_hdr) +
+ 				sizeof(struct icmsg_hdr)];
+ 
+ 			if (negop->icversion_data[1].major < 5)
+ 				negop->icframe_vercnt = 0;
+ 		} else {
+ 			vss_msg = (struct hv_vss_msg *)&recv_buffer[
+ 				sizeof(struct vmbuspipe_hdr) +
+ 				sizeof(struct icmsg_hdr)];
+ 
+ 			/*
+ 			 * Stash away this global state for completing the
+ 			 * transaction; note transactions are serialized.
+ 			 */
+ 
+ 			vss_transaction.recv_len = recvlen;
+ 			vss_transaction.recv_channel = channel;
+ 			vss_transaction.recv_req_id = requestid;
+ 			vss_transaction.active = true;
+ 			vss_transaction.msg = (struct hv_vss_msg *)vss_msg;
+ 
+ 			switch (vss_msg->vss_hdr.operation) {
+ 				/*
+ 				 * Initiate a "freeze/thaw"
+ 				 * operation in the guest.
+ 				 * We respond to the host once
+ 				 * the operation is complete.
+ 				 *
+ 				 * We send the message to the
+ 				 * user space daemon and the
+ 				 * operation is performed in
+ 				 * the daemon.
+ 				 */
+ 			case VSS_OP_FREEZE:
+ 			case VSS_OP_THAW:
+ 				schedule_work(&vss_send_op_work);
+ 				return;
+ 
+ 			case VSS_OP_HOT_BACKUP:
+ 				vss_msg->vss_cf.flags =
+ 					 VSS_HBU_NO_AUTO_RECOVERY;
+ 				vss_respond_to_host(0);
+ 				return;
+ 
+ 			case VSS_OP_GET_DM_INFO:
+ 				vss_msg->dm_info.flags = 0;
+ 				vss_respond_to_host(0);
+ 				return;
+ 
+ 			default:
+ 				vss_respond_to_host(0);
+ 				return;
+ 
+ 			}
+ 
+ 		}
+ 
+ 		icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
+ 			| ICMSGHDRFLAG_RESPONSE;
+ 
+ 		vmbus_sendpacket(channel, recv_buffer,
+ 				       recvlen, requestid,
+ 				       VM_PKT_DATA_INBAND, 0);
+ 	}
+ 
+ }
+ 
+ int
+ hv_vss_init(struct hv_util_service *srv)
+ {
+ 	int err;
+ 
+ 	err = cn_add_callback(&vss_id, vss_name, vss_cn_callback);
+ 	if (err)
+ 		return err;
+ 	recv_buffer = srv->recv_buffer;
+ 
+ 	/*
+ 	 * When this driver loads, the user level daemon that
+ 	 * processes the host requests may not yet be running.
+ 	 * Defer processing channel callbacks until the daemon
+ 	 * has registered.
+ 	 */
+ 	vss_transaction.active = true;
+ 	return 0;
+ }
+ 
+ void hv_vss_deinit(void)
+ {
+ 	cn_del_callback(&vss_id);
+ 	cancel_work_sync(&vss_send_op_work);
+ }
diff -crN linux-3.8.6-gentoo/drivers/hv/hv_util.c linux-3.8.6-gentoo-hyperv/drivers/hv/hv_util.c
*** linux-3.8.6-gentoo/drivers/hv/hv_util.c	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/drivers/hv/hv_util.c	Sat Apr 13 02:24:31 2013
***************
*** 49,54 ****
--- 49,70 ----
  	.util_deinit = hv_kvp_deinit,
  };
  
+ static struct hv_util_service util_vss = {
+ 	.util_cb = hv_vss_onchannelcallback,
+ 	.util_init = hv_vss_init,
+ 	.util_deinit = hv_vss_deinit,
+ };
+ 
+ static void perform_shutdown(struct work_struct *dummy)
+ {
+ 	orderly_poweroff(true);
+ }
+ 
+ /*
+  * Perform the shutdown operation in a thread context.
+  */
+ static DECLARE_WORK(shutdown_work, perform_shutdown);
+ 
  static void shutdown_onchannelcallback(void *context)
  {
  	struct vmbus_channel *channel = context;
***************
*** 106,112 ****
  	}
  
  	if (execute_shutdown == true)
! 		orderly_poweroff(true);
  }
  
  /*
--- 122,128 ----
  	}
  
  	if (execute_shutdown == true)
! 		schedule_work(&shutdown_work);
  }
  
  /*
***************
*** 274,279 ****
--- 290,305 ----
  		}
  	}
  
+ 	/*
+ 	 * The set of services managed by the util driver are not performance
+ 	 * critical and do not need batched reading. Furthermore, some services
+ 	 * such as KVP can only handle one message from the host at a time.
+ 	 * Turn off batched reading for all util drivers before we open the
+ 	 * channel.
+ 	 */
+ 
+ 	set_channel_read_state(dev->channel, false);
+ 
  	ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0,
  			srv->util_cb, dev->channel);
  	if (ret)
***************
*** 304,324 ****
  
  static const struct hv_vmbus_device_id id_table[] = {
  	/* Shutdown guid */
! 	{ VMBUS_DEVICE(0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49,
! 		       0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB)
! 	  .driver_data = (unsigned long)&util_shutdown },
  	/* Time synch guid */
! 	{ VMBUS_DEVICE(0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49,
! 		       0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf)
! 	  .driver_data = (unsigned long)&util_timesynch },
  	/* Heartbeat guid */
! 	{ VMBUS_DEVICE(0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e,
! 		       0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d)
! 	  .driver_data = (unsigned long)&util_heartbeat },
  	/* KVP guid */
! 	{ VMBUS_DEVICE(0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d,
! 		       0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3,  0xe6)
! 	  .driver_data = (unsigned long)&util_kvp },
  	{ },
  };
  
--- 330,354 ----
  
  static const struct hv_vmbus_device_id id_table[] = {
  	/* Shutdown guid */
! 	{ HV_SHUTDOWN_GUID,
! 	  .driver_data = (unsigned long)&util_shutdown
! 	},
  	/* Time synch guid */
! 	{ HV_TS_GUID,
! 	  .driver_data = (unsigned long)&util_timesynch
! 	},
  	/* Heartbeat guid */
! 	{ HV_HEART_BEAT_GUID,
! 	  .driver_data = (unsigned long)&util_heartbeat
! 	},
  	/* KVP guid */
! 	{ HV_KVP_GUID,
! 	  .driver_data = (unsigned long)&util_kvp
! 	},
! 	/* VSS GUID */
! 	{ HV_VSS_GUID,
! 	  .driver_data = (unsigned long)&util_vss
! 	},
  	{ },
  };
  
diff -crN linux-3.8.6-gentoo/drivers/hv/hyperv_vmbus.h linux-3.8.6-gentoo-hyperv/drivers/hv/hyperv_vmbus.h
*** linux-3.8.6-gentoo/drivers/hv/hyperv_vmbus.h	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/drivers/hv/hyperv_vmbus.h	Sat Apr 13 02:25:04 2013
***************
*** 101,115 ****
  /* 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;
--- 101,106 ----
***************
*** 338,350 ****
  	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.
--- 329,334 ----
***************
*** 498,508 ****
  
  
  
- 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
--- 482,487 ----
***************
*** 513,528 ****
  
  	bool synic_initialized;
  
- 	/*
- 	 * This is used as an input param to HvCallSignalEvent hypercall. The
- 	 * input param is immutable in our usage and must be dynamic mem (vs
- 	 * stack or global). */
- 	struct hv_input_signal_event_buffer *signal_event_buffer;
- 	/* 8-bytes aligned of the buffer above */
- 	struct hv_input_signal_event *signal_event_param;
- 
  	void *synic_message_page[NR_CPUS];
  	void *synic_event_page[NR_CPUS];
  };
  
  extern struct hv_context hv_context;
--- 492,515 ----
  
  	bool synic_initialized;
  
  	void *synic_message_page[NR_CPUS];
  	void *synic_event_page[NR_CPUS];
+ 	/*
+ 	 * Hypervisor's notion of virtual processor ID is different from
+ 	 * Linux' notion of CPU ID. This information can only be retrieved
+ 	 * in the context of the calling CPU. Setup a map for easy access
+ 	 * to this information:
+ 	 *
+ 	 * vp_index[a] is the Hyper-V's processor ID corresponding to
+ 	 * Linux cpuid 'a'.
+ 	 */
+ 	u32 vp_index[NR_CPUS];
+ 	/*
+ 	 * Starting with win8, we can take channel interrupts on any CPU;
+ 	 * we will manage the tasklet that handles events on a per CPU
+ 	 * basis.
+ 	 */
+ 	struct tasklet_struct *event_dpc[NR_CPUS];
  };
  
  extern struct hv_context hv_context;
***************
*** 538,549 ****
  			 enum hv_message_type message_type,
  			 void *payload, size_t payload_size);
  
! extern u16 hv_signal_event(void);
  
  extern void hv_synic_init(void *irqarg);
  
  extern void hv_synic_cleanup(void *arg);
  
  
  /* Interface */
  
--- 525,543 ----
  			 enum hv_message_type message_type,
  			 void *payload, size_t payload_size);
  
! extern u16 hv_signal_event(void *con_id);
  
  extern void hv_synic_init(void *irqarg);
  
  extern void hv_synic_cleanup(void *arg);
  
+ /*
+  * Host version information.
+  */
+ extern unsigned int host_info_eax;
+ extern unsigned int host_info_ebx;
+ extern unsigned int host_info_ecx;
+ extern unsigned int host_info_edx;
  
  /* Interface */
  
***************
*** 555,561 ****
  
  int hv_ringbuffer_write(struct hv_ring_buffer_info *ring_info,
  		    struct scatterlist *sglist,
! 		    u32 sgcount);
  
  int hv_ringbuffer_peek(struct hv_ring_buffer_info *ring_info, void *buffer,
  		   u32 buflen);
--- 549,555 ----
  
  int hv_ringbuffer_write(struct hv_ring_buffer_info *ring_info,
  		    struct scatterlist *sglist,
! 		    u32 sgcount, bool *signal);
  
  int hv_ringbuffer_peek(struct hv_ring_buffer_info *ring_info, void *buffer,
  		   u32 buflen);
***************
*** 563,575 ****
  int hv_ringbuffer_read(struct hv_ring_buffer_info *ring_info,
  		   void *buffer,
  		   u32 buflen,
! 		   u32 offset);
  
- u32 hv_get_ringbuffer_interrupt_mask(struct hv_ring_buffer_info *ring_info);
  
  void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info,
  			    struct hv_ring_buffer_debug_info *debug_info);
  
  /*
   * Maximum channels is determined by the size of the interrupt page
   * which is PAGE_SIZE. 1/2 of PAGE_SIZE is for send endpoint interrupt
--- 557,572 ----
  int hv_ringbuffer_read(struct hv_ring_buffer_info *ring_info,
  		   void *buffer,
  		   u32 buflen,
! 		   u32 offset, bool *signal);
  
  
  void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info,
  			    struct hv_ring_buffer_debug_info *debug_info);
  
+ void hv_begin_read(struct hv_ring_buffer_info *rbi);
+ 
+ u32 hv_end_read(struct hv_ring_buffer_info *rbi);
+ 
  /*
   * 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
***************
*** 657,663 ****
  
  int vmbus_post_msg(void *buffer, size_t buflen);
  
! int vmbus_set_event(u32 child_relid);
  
  void vmbus_on_event(unsigned long data);
  
--- 654,660 ----
  
  int vmbus_post_msg(void *buffer, size_t buflen);
  
! int vmbus_set_event(struct vmbus_channel *channel);
  
  void vmbus_on_event(unsigned long data);
  
diff -crN linux-3.8.6-gentoo/drivers/hv/ring_buffer.c linux-3.8.6-gentoo-hyperv/drivers/hv/ring_buffer.c
*** linux-3.8.6-gentoo/drivers/hv/ring_buffer.c	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/drivers/hv/ring_buffer.c	Sat Apr 13 02:24:31 2013
***************
*** 29,34 ****
--- 29,134 ----
  
  #include "hyperv_vmbus.h"
  
+ void hv_begin_read(struct hv_ring_buffer_info *rbi)
+ {
+ 	rbi->ring_buffer->interrupt_mask = 1;
+ 	smp_mb();
+ }
+ 
+ u32 hv_end_read(struct hv_ring_buffer_info *rbi)
+ {
+ 	u32 read;
+ 	u32 write;
+ 
+ 	rbi->ring_buffer->interrupt_mask = 0;
+ 	smp_mb();
+ 
+ 	/*
+ 	 * Now check to see if the ring buffer is still empty.
+ 	 * If it is not, we raced and we need to process new
+ 	 * incoming messages.
+ 	 */
+ 	hv_get_ringbuffer_availbytes(rbi, &read, &write);
+ 
+ 	return read;
+ }
+ 
+ /*
+  * When we write to the ring buffer, check if the host needs to
+  * be signaled. Here is the details of this protocol:
+  *
+  *	1. The host guarantees that while it is draining the
+  *	   ring buffer, it will set the interrupt_mask to
+  *	   indicate it does not need to be interrupted when
+  *	   new data is placed.
+  *
+  *	2. The host guarantees that it will completely drain
+  *	   the ring buffer before exiting the read loop. Further,
+  *	   once the ring buffer is empty, it will clear the
+  *	   interrupt_mask and re-check to see if new data has
+  *	   arrived.
+  */
+ 
+ static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi)
+ {
+ 	smp_mb();
+ 	if (rbi->ring_buffer->interrupt_mask)
+ 		return false;
+ 
+ 	/*
+ 	 * This is the only case we need to signal when the
+ 	 * ring transitions from being empty to non-empty.
+ 	 */
+ 	if (old_write == rbi->ring_buffer->read_index)
+ 		return true;
+ 
+ 	return false;
+ }
+ 
+ /*
+  * To optimize the flow management on the send-side,
+  * when the sender is blocked because of lack of
+  * sufficient space in the ring buffer, potential the
+  * consumer of the ring buffer can signal the producer.
+  * This is controlled by the following parameters:
+  *
+  * 1. pending_send_sz: This is the size in bytes that the
+  *    producer is trying to send.
+  * 2. The feature bit feat_pending_send_sz set to indicate if
+  *    the consumer of the ring will signal when the ring
+  *    state transitions from being full to a state where
+  *    there is room for the producer to send the pending packet.
+  */
+ 
+ static bool hv_need_to_signal_on_read(u32 old_rd,
+ 					 struct hv_ring_buffer_info *rbi)
+ {
+ 	u32 prev_write_sz;
+ 	u32 cur_write_sz;
+ 	u32 r_size;
+ 	u32 write_loc = rbi->ring_buffer->write_index;
+ 	u32 read_loc = rbi->ring_buffer->read_index;
+ 	u32 pending_sz = rbi->ring_buffer->pending_send_sz;
+ 
+ 	/*
+ 	 * If the other end is not blocked on write don't bother.
+ 	 */
+ 	if (pending_sz == 0)
+ 		return false;
+ 
+ 	r_size = rbi->ring_datasize;
+ 	cur_write_sz = write_loc >= read_loc ? r_size - (write_loc - read_loc) :
+ 			read_loc - write_loc;
+ 
+ 	prev_write_sz = write_loc >= old_rd ? r_size - (write_loc - old_rd) :
+ 			old_rd - write_loc;
+ 
+ 
+ 	if ((prev_write_sz < pending_sz) && (cur_write_sz >= pending_sz))
+ 		return true;
+ 
+ 	return false;
+ }
  
  /*
   * hv_get_next_write_location()
***************
*** 239,257 ****
  	}
  }
  
- 
- /*
-  *
-  * hv_get_ringbuffer_interrupt_mask()
-  *
-  * Get the interrupt mask for the specified ring buffer
-  *
-  */
- u32 hv_get_ringbuffer_interrupt_mask(struct hv_ring_buffer_info *rbi)
- {
- 	return rbi->ring_buffer->interrupt_mask;
- }
- 
  /*
   *
   * hv_ringbuffer_init()
--- 339,344 ----
***************
*** 298,304 ****
   *
   */
  int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
! 		    struct scatterlist *sglist, u32 sgcount)
  {
  	int i = 0;
  	u32 bytes_avail_towrite;
--- 385,391 ----
   *
   */
  int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
! 		    struct scatterlist *sglist, u32 sgcount, bool *signal)
  {
  	int i = 0;
  	u32 bytes_avail_towrite;
***************
*** 307,312 ****
--- 394,400 ----
  
  	struct scatterlist *sg;
  	u32 next_write_location;
+ 	u32 old_write;
  	u64 prev_indices = 0;
  	unsigned long flags;
  
***************
*** 335,340 ****
--- 423,430 ----
  	/* Write to the ring buffer */
  	next_write_location = hv_get_next_write_location(outring_info);
  
+ 	old_write = next_write_location;
+ 
  	for_each_sg(sglist, sg, sgcount, i)
  	{
  		next_write_location = hv_copyto_ringbuffer(outring_info,
***************
*** 351,364 ****
  					     &prev_indices,
  					     sizeof(u64));
  
! 	/* Make sure we flush all writes before updating the writeIndex */
! 	smp_wmb();
  
  	/* Now, update the write location */
  	hv_set_next_write_location(outring_info, next_write_location);
  
  
  	spin_unlock_irqrestore(&outring_info->ring_lock, flags);
  	return 0;
  }
  
--- 441,456 ----
  					     &prev_indices,
  					     sizeof(u64));
  
! 	/* Issue a full memory barrier before updating the write index */
! 	smp_mb();
  
  	/* Now, update the write location */
  	hv_set_next_write_location(outring_info, next_write_location);
  
  
  	spin_unlock_irqrestore(&outring_info->ring_lock, flags);
+ 
+ 	*signal = hv_need_to_signal(old_write, outring_info);
  	return 0;
  }
  
***************
*** 414,426 ****
   *
   */
  int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer,
! 		   u32 buflen, u32 offset)
  {
  	u32 bytes_avail_towrite;
  	u32 bytes_avail_toread;
  	u32 next_read_location = 0;
  	u64 prev_indices = 0;
  	unsigned long flags;
  
  	if (buflen <= 0)
  		return -EINVAL;
--- 506,519 ----
   *
   */
  int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer,
! 		   u32 buflen, u32 offset, bool *signal)
  {
  	u32 bytes_avail_towrite;
  	u32 bytes_avail_toread;
  	u32 next_read_location = 0;
  	u64 prev_indices = 0;
  	unsigned long flags;
+ 	u32 old_read;
  
  	if (buflen <= 0)
  		return -EINVAL;
***************
*** 431,436 ****
--- 524,531 ----
  				&bytes_avail_toread,
  				&bytes_avail_towrite);
  
+ 	old_read = bytes_avail_toread;
+ 
  	/* Make sure there is something to read */
  	if (bytes_avail_toread < buflen) {
  		spin_unlock_irqrestore(&inring_info->ring_lock, flags);
***************
*** 461,465 ****
--- 556,562 ----
  
  	spin_unlock_irqrestore(&inring_info->ring_lock, flags);
  
+ 	*signal = hv_need_to_signal_on_read(old_read, inring_info);
+ 
  	return 0;
  }
diff -crN linux-3.8.6-gentoo/drivers/hv/vmbus_drv.c linux-3.8.6-gentoo-hyperv/drivers/hv/vmbus_drv.c
*** linux-3.8.6-gentoo/drivers/hv/vmbus_drv.c	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/drivers/hv/vmbus_drv.c	Sat Apr 13 02:24:31 2013
***************
*** 33,47 ****
  #include <acpi/acpi_bus.h>
  #include <linux/completion.h>
  #include <linux/hyperv.h>
  #include <asm/hyperv.h>
  #include <asm/hypervisor.h>
  #include "hyperv_vmbus.h"
  
  
  static struct acpi_device  *hv_acpi_dev;
  
  static struct tasklet_struct msg_dpc;
- static struct tasklet_struct event_dpc;
  static struct completion probe_event;
  static int irq;
  
--- 33,48 ----
  #include <acpi/acpi_bus.h>
  #include <linux/completion.h>
  #include <linux/hyperv.h>
+ #include <linux/kernel_stat.h>
  #include <asm/hyperv.h>
  #include <asm/hypervisor.h>
+ #include <asm/mshyperv.h>
  #include "hyperv_vmbus.h"
  
  
  static struct acpi_device  *hv_acpi_dev;
  
  static struct tasklet_struct msg_dpc;
  static struct completion probe_event;
  static int irq;
  
***************
*** 454,474 ****
  	union hv_synic_event_flags *event;
  	bool handled = false;
  
  	/*
  	 * Check for events before checking for messages. This is the order
  	 * in which events and messages are checked in Windows guests on
  	 * Hyper-V, and the Windows team suggested we do the same.
  	 */
  
! 	page_addr = hv_context.synic_event_page[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])) {
  		handled = true;
- 		tasklet_schedule(&event_dpc);
  	}
  
  	page_addr = hv_context.synic_message_page[cpu];
  	msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT;
  
--- 455,494 ----
  	union hv_synic_event_flags *event;
  	bool handled = false;
  
+ 	page_addr = hv_context.synic_event_page[cpu];
+ 	if (page_addr == NULL)
+ 		return IRQ_NONE;
+ 
+ 	event = (union hv_synic_event_flags *)page_addr +
+ 					 VMBUS_MESSAGE_SINT;
  	/*
  	 * Check for events before checking for messages. This is the order
  	 * in which events and messages are checked in Windows guests on
  	 * Hyper-V, and the Windows team suggested we do the same.
  	 */
  
! 	if ((vmbus_proto_version == VERSION_WS2008) ||
! 		(vmbus_proto_version == VERSION_WIN7)) {
  
! 		/* Since we are a child, we only need to check bit 0 */
! 		if (sync_test_and_clear_bit(0,
! 			(unsigned long *) &event->flags32[0])) {
! 			handled = true;
! 		}
! 	} else {
! 		/*
! 		 * Our host is win8 or above. The signaling mechanism
! 		 * has changed and we can directly look at the event page.
! 		 * If bit n is set then we have an interrup on the channel
! 		 * whose id is n.
! 		 */
  		handled = true;
  	}
  
+ 	if (handled)
+ 		tasklet_schedule(hv_context.event_dpc[cpu]);
+ 
+ 
  	page_addr = hv_context.synic_message_page[cpu];
  	msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT;
  
***************
*** 485,490 ****
--- 505,523 ----
  }
  
  /*
+  * vmbus interrupt flow handler:
+  * vmbus interrupts can concurrently occur on multiple CPUs and
+  * can be handled concurrently.
+  */
+ 
+ static void vmbus_flow_handler(unsigned int irq, struct irq_desc *desc)
+ {
+ 	kstat_incr_irqs_this_cpu(irq, desc);
+ 
+ 	desc->action->handler(irq, desc->action->dev_id);
+ }
+ 
+ /*
   * vmbus_bus_init -Main vmbus driver initialization routine.
   *
   * Here, we
***************
*** 496,502 ****
  static int vmbus_bus_init(int irq)
  {
  	int ret;
- 	unsigned int vector;
  
  	/* Hypervisor initialization...setup hypercall page..etc */
  	ret = hv_init();
--- 529,534 ----
***************
*** 506,512 ****
  	}
  
  	tasklet_init(&msg_dpc, vmbus_on_msg_dpc, 0);
- 	tasklet_init(&event_dpc, vmbus_on_event, 0);
  
  	ret = bus_register(&hv_bus);
  	if (ret)
--- 538,543 ----
***************
*** 520,532 ****
  		goto err_unregister;
  	}
  
! 	vector = IRQ0_VECTOR + irq;
  
  	/*
! 	 * Notify the hypervisor of our irq and
  	 * connect to the host.
  	 */
! 	on_each_cpu(hv_synic_init, (void *)&vector, 1);
  	ret = vmbus_connect();
  	if (ret)
  		goto err_irq;
--- 551,573 ----
  		goto err_unregister;
  	}
  
! 	/*
! 	 * Vmbus interrupts can be handled concurrently on
! 	 * different CPUs. Establish an appropriate interrupt flow
! 	 * handler that can support this model.
! 	 */
! 	irq_set_handler(irq, vmbus_flow_handler);
! 
! 	/*
! 	 * Register our interrupt handler.
! 	 */
! 	hv_register_vmbus_handler(irq, vmbus_isr);
  
  	/*
! 	 * Initialize the per-cpu interrupt state and
  	 * connect to the host.
  	 */
! 	on_each_cpu(hv_synic_init, NULL, 1);
  	ret = vmbus_connect();
  	if (ret)
  		goto err_irq;
***************
*** 575,582 ****
  
  	ret = driver_register(&hv_driver->driver);
  
- 	vmbus_request_offers();
- 
  	return ret;
  }
  EXPORT_SYMBOL_GPL(__vmbus_driver_register);
--- 616,621 ----
diff -crN linux-3.8.6-gentoo/drivers/net/hyperv/netvsc_drv.c linux-3.8.6-gentoo-hyperv/drivers/net/hyperv/netvsc_drv.c
*** linux-3.8.6-gentoo/drivers/net/hyperv/netvsc_drv.c	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/drivers/net/hyperv/netvsc_drv.c	Sat Apr 13 02:24:34 2013
***************
*** 193,202 ****
  	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
  		const skb_frag_t *f = &skb_shinfo(skb)->frags[i];
  
! 		packet->page_buf[i+npg_data+1].pfn =
! 			page_to_pfn(skb_frag_page(f));
  		packet->page_buf[i+npg_data+1].offset = f->page_offset;
! 		packet->page_buf[i+npg_data+1].len = skb_frag_size(f);
  	}
  
  	/* Set the completion routine */
--- 193,201 ----
  	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
  		const skb_frag_t *f = &skb_shinfo(skb)->frags[i];
  
! 		packet->page_buf[i+npg_data+1].pfn = page_to_pfn(skb_frag_page(f));
  		packet->page_buf[i+npg_data+1].offset = f->page_offset;
! 		packet->page_buf[i+npg_data+1].len = f->size;
  	}
  
  	/* Set the completion routine */
***************
*** 498,505 ****
  
  static const struct hv_vmbus_device_id id_table[] = {
  	/* Network guid */
! 	{ VMBUS_DEVICE(0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46,
! 		       0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E) },
  	{ },
  };
  
--- 497,503 ----
  
  static const struct hv_vmbus_device_id id_table[] = {
  	/* Network guid */
! 	{ HV_NIC_GUID, },
  	{ },
  };
  
diff -crN linux-3.8.6-gentoo/drivers/scsi/storvsc_drv.c linux-3.8.6-gentoo-hyperv/drivers/scsi/storvsc_drv.c
*** linux-3.8.6-gentoo/drivers/scsi/storvsc_drv.c	Sat Apr 13 02:03:29 2013
--- linux-3.8.6-gentoo-hyperv/drivers/scsi/storvsc_drv.c	Sat Apr 13 02:24:30 2013
***************
*** 201,206 ****
--- 201,207 ----
  #define SRB_STATUS_AUTOSENSE_VALID	0x80
  #define SRB_STATUS_INVALID_LUN	0x20
  #define SRB_STATUS_SUCCESS	0x01
+ #define SRB_STATUS_ABORTED	0x02
  #define SRB_STATUS_ERROR	0x04
  
  /*
***************
*** 295,300 ****
--- 296,320 ----
  	uint lun;
  };
  
+ static void storvsc_device_scan(struct work_struct *work)
+ {
+ 	struct storvsc_scan_work *wrk;
+ 	uint lun;
+ 	struct scsi_device *sdev;
+ 
+ 	wrk = container_of(work, struct storvsc_scan_work, work);
+ 	lun = wrk->lun;
+ 
+ 	sdev = scsi_device_lookup(wrk->host, 0, 0, lun);
+ 	if (!sdev)
+ 		goto done;
+ 	scsi_rescan_device(&sdev->sdev_gendev);
+ 	scsi_device_put(sdev);
+ 
+ done:
+ 	kfree(wrk);
+ }
+ 
  static void storvsc_bus_scan(struct work_struct *work)
  {
  	struct storvsc_scan_work *wrk;
***************
*** 761,766 ****
--- 781,846 ----
  	return ret;
  }
  
+ static void storvsc_handle_error(struct vmscsi_request *vm_srb,
+ 				struct scsi_cmnd *scmnd,
+ 				struct Scsi_Host *host,
+ 				u8 asc, u8 ascq)
+ {
+ 	struct storvsc_scan_work *wrk;
+ 	void (*process_err_fn)(struct work_struct *work);
+ 	bool do_work = false;
+ 
+ 	switch (vm_srb->srb_status) {
+ 	case SRB_STATUS_ERROR:
+ 		/*
+ 		 * If there is an error; offline the device since all
+ 		 * error recovery strategies would have already been
+ 		 * deployed on the host side. However, if the command
+ 		 * were a pass-through command deal with it appropriately.
+ 		 */
+ 		switch (scmnd->cmnd[0]) {
+ 		case ATA_16:
+ 		case ATA_12:
+ 			set_host_byte(scmnd, DID_PASSTHROUGH);
+ 			break;
+ 		default:
+ 			set_host_byte(scmnd, DID_TARGET_FAILURE);
+ 		}
+ 		break;
+ 	case SRB_STATUS_INVALID_LUN:
+ 		do_work = true;
+ 		process_err_fn = storvsc_remove_lun;
+ 		break;
+ 	case (SRB_STATUS_ABORTED | SRB_STATUS_AUTOSENSE_VALID):
+ 		if ((asc == 0x2a) && (ascq == 0x9)) {
+ 			do_work = true;
+ 			process_err_fn = storvsc_device_scan;
+ 			/*
+ 			 * Retry the I/O that trigerred this.
+ 			 */
+ 			set_host_byte(scmnd, DID_REQUEUE);
+ 		}
+ 		break;
+ 	}
+ 
+ 	if (!do_work)
+ 		return;
+ 
+ 	/*
+ 	 * We need to schedule work to process this error; schedule it.
+ 	 */
+ 	wrk = kmalloc(sizeof(struct storvsc_scan_work), GFP_ATOMIC);
+ 	if (!wrk) {
+ 		set_host_byte(scmnd, DID_TARGET_FAILURE);
+ 		return;
+ 	}
+ 
+ 	wrk->host = host;
+ 	wrk->lun = vm_srb->lun;
+ 	INIT_WORK(&wrk->work, process_err_fn);
+ 	schedule_work(&wrk->work);
+ }
+ 
  
  static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request)
  {
***************
*** 769,776 ****
  	void (*scsi_done_fn)(struct scsi_cmnd *);
  	struct scsi_sense_hdr sense_hdr;
  	struct vmscsi_request *vm_srb;
- 	struct storvsc_scan_work *wrk;
  	struct stor_mem_pools *memp = scmnd->device->hostdata;
  
  	vm_srb = &cmd_request->vstor_packet.vm_srb;
  	if (cmd_request->bounce_sgl_count) {
--- 849,861 ----
  	void (*scsi_done_fn)(struct scsi_cmnd *);
  	struct scsi_sense_hdr sense_hdr;
  	struct vmscsi_request *vm_srb;
  	struct stor_mem_pools *memp = scmnd->device->hostdata;
+ 	struct Scsi_Host *host;
+ 	struct storvsc_device *stor_dev;
+ 	struct hv_device *dev = host_dev->dev;
+ 
+ 	stor_dev = get_in_stor_device(dev);
+ 	host = stor_dev->host;
  
  	vm_srb = &cmd_request->vstor_packet.vm_srb;
  	if (cmd_request->bounce_sgl_count) {
***************
*** 783,837 ****
  					cmd_request->bounce_sgl_count);
  	}
  
- 	/*
- 	 * If there is an error; offline the device since all
- 	 * error recovery strategies would have already been
- 	 * deployed on the host side. However, if the command
- 	 * were a pass-through command deal with it appropriately.
- 	 */
  	scmnd->result = vm_srb->scsi_status;
  
- 	if (vm_srb->srb_status == SRB_STATUS_ERROR) {
- 		switch (scmnd->cmnd[0]) {
- 		case ATA_16:
- 		case ATA_12:
- 			set_host_byte(scmnd, DID_PASSTHROUGH);
- 			break;
- 		default:
- 			set_host_byte(scmnd, DID_TARGET_FAILURE);
- 		}
- 	}
- 
- 
- 	/*
- 	 * If the LUN is invalid; remove the device.
- 	 */
- 	if (vm_srb->srb_status == SRB_STATUS_INVALID_LUN) {
- 		struct storvsc_device *stor_dev;
- 		struct hv_device *dev = host_dev->dev;
- 		struct Scsi_Host *host;
- 
- 		stor_dev = get_in_stor_device(dev);
- 		host = stor_dev->host;
- 
- 		wrk = kmalloc(sizeof(struct storvsc_scan_work),
- 				GFP_ATOMIC);
- 		if (!wrk) {
- 			scmnd->result = DID_TARGET_FAILURE << 16;
- 		} else {
- 			wrk->host = host;
- 			wrk->lun = vm_srb->lun;
- 			INIT_WORK(&wrk->work, storvsc_remove_lun);
- 			schedule_work(&wrk->work);
- 		}
- 	}
- 
  	if (scmnd->result) {
  		if (scsi_normalize_sense(scmnd->sense_buffer,
  				SCSI_SENSE_BUFFERSIZE, &sense_hdr))
  			scsi_print_sense_hdr("storvsc", &sense_hdr);
  	}
  
  	scsi_set_resid(scmnd,
  		cmd_request->data_buffer.len -
  		vm_srb->data_transfer_length);
--- 868,885 ----
  					cmd_request->bounce_sgl_count);
  	}
  
  	scmnd->result = vm_srb->scsi_status;
  
  	if (scmnd->result) {
  		if (scsi_normalize_sense(scmnd->sense_buffer,
  				SCSI_SENSE_BUFFERSIZE, &sense_hdr))
  			scsi_print_sense_hdr("storvsc", &sense_hdr);
  	}
  
+ 	if (vm_srb->srb_status != SRB_STATUS_SUCCESS)
+ 		storvsc_handle_error(vm_srb, scmnd, host, sense_hdr.asc,
+ 					 sense_hdr.ascq);
+ 
  	scsi_set_resid(scmnd,
  		cmd_request->data_buffer.len -
  		vm_srb->data_transfer_length);
***************
*** 1156,1161 ****
--- 1204,1211 ----
  
  	blk_queue_bounce_limit(sdevice->request_queue, BLK_BOUNCE_ANY);
  
+ 	sdevice->no_write_same = 1;
+ 
  	return 0;
  }
  
***************
*** 1238,1243 ****
--- 1288,1295 ----
  	u8 scsi_op = scmnd->cmnd[0];
  
  	switch (scsi_op) {
+ 	/* the host does not handle WRITE_SAME, log accident usage */
+ 	case WRITE_SAME:
  	/*
  	 * smartd sends this command and the host does not handle
  	 * this. So, don't send it.
***************
*** 1411,1423 ****
  
  static const struct hv_vmbus_device_id id_table[] = {
  	/* SCSI guid */
! 	{ VMBUS_DEVICE(0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d,
! 		       0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f)
! 	  .driver_data = SCSI_GUID },
  	/* IDE guid */
! 	{ VMBUS_DEVICE(0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44,
! 		       0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5)
! 	  .driver_data = IDE_GUID },
  	{ },
  };
  
--- 1463,1475 ----
  
  static const struct hv_vmbus_device_id id_table[] = {
  	/* SCSI guid */
! 	{ HV_SCSI_GUID,
! 	  .driver_data = SCSI_GUID
! 	},
  	/* IDE guid */
! 	{ HV_IDE_GUID,
! 	  .driver_data = IDE_GUID
! 	},
  	{ },
  };
  
diff -crN linux-3.8.6-gentoo/drivers/video/Kconfig linux-3.8.6-gentoo-hyperv/drivers/video/Kconfig
*** linux-3.8.6-gentoo/drivers/video/Kconfig	Sat Apr 13 02:03:29 2013
--- linux-3.8.6-gentoo-hyperv/drivers/video/Kconfig	Sat Apr 13 02:25:45 2013
***************
*** 736,742 ****
  
  config FB_UVESA
  	tristate "Userspace VESA VGA graphics support"
! 	depends on FB && CONNECTOR
  	select FB_CFB_FILLRECT
  	select FB_CFB_COPYAREA
  	select FB_CFB_IMAGEBLIT
--- 736,742 ----
  
  config FB_UVESA
  	tristate "Userspace VESA VGA graphics support"
! 	depends on FB && CONNECTOR && !FB_HYPERV
  	select FB_CFB_FILLRECT
  	select FB_CFB_COPYAREA
  	select FB_CFB_IMAGEBLIT
***************
*** 2418,2423 ****
--- 2418,2432 ----
  	  Choose this option if you want to use the Unigfx device as a
  	  framebuffer device. Without the support of PCI & AGP.
  
+ config FB_HYPERV
+ 	tristate "Microsoft Hyper-V Synthetic Video support"
+ 	depends on FB && HYPERV
+ 	select FB_CFB_FILLRECT
+ 	select FB_CFB_COPYAREA
+ 	select FB_CFB_IMAGEBLIT
+ 	help
+ 	  This driver supports Microsoft Synthetic Hyper-V Video.
+ 
  source "drivers/video/omap/Kconfig"
  source "drivers/video/omap2/Kconfig"
  source "drivers/video/exynos/Kconfig"
diff -crN linux-3.8.6-gentoo/drivers/video/Makefile linux-3.8.6-gentoo-hyperv/drivers/video/Makefile
*** linux-3.8.6-gentoo/drivers/video/Makefile	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/drivers/video/Makefile	Sat Apr 13 02:25:33 2013
***************
*** 146,151 ****
--- 146,152 ----
  obj-$(CONFIG_FB_NUC900)           += nuc900fb.o
  obj-$(CONFIG_FB_JZ4740)		  += jz4740_fb.o
  obj-$(CONFIG_FB_PUV3_UNIGFX)      += fb-puv3.o
+ obj-$(CONFIG_FB_HYPERV)		  += hyperv_fb.o
  
  # Platform or fallback drivers go here
  obj-$(CONFIG_FB_UVESA)            += uvesafb.o
diff -crN linux-3.8.6-gentoo/drivers/video/hyperv_fb.c linux-3.8.6-gentoo-hyperv/drivers/video/hyperv_fb.c
*** linux-3.8.6-gentoo/drivers/video/hyperv_fb.c	Thu Jan  1 09:00:00 1970
--- linux-3.8.6-gentoo-hyperv/drivers/video/hyperv_fb.c	Sat Apr 13 02:24:39 2013
***************
*** 0 ****
--- 1,829 ----
+ /*
+  * Copyright (c) 2012, Microsoft Corporation.
+  *
+  * Author:
+  *   Haiyang Zhang <haiyangz@microsoft.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.
+  */
+ 
+ /*
+  * Hyper-V Synthetic Video Frame Buffer Driver
+  *
+  * This is the driver for the Hyper-V Synthetic Video, which supports
+  * screen resolution up to Full HD 1920x1080 with 32 bit color on Windows
+  * Server 2012, and 1600x1200 with 16 bit color on Windows Server 2008 R2
+  * or earlier.
+  *
+  * It also solves the double mouse cursor issue of the emulated video mode.
+  *
+  * The default screen resolution is 1152x864, which may be changed by a
+  * kernel parameter:
+  *     video=hyperv_fb:<width>x<height>
+  *     For example: video=hyperv_fb:1280x1024
+  *
+  * Portrait orientation is also supported:
+  *     For example: video=hyperv_fb:864x1152
+  */
+ 
+ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+ 
+ #include <linux/module.h>
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+ #include <linux/completion.h>
+ #include <linux/fb.h>
+ #include <linux/pci.h>
+ 
+ #include <linux/hyperv.h>
+ 
+ 
+ /* Hyper-V Synthetic Video Protocol definitions and structures */
+ #define MAX_VMBUS_PKT_SIZE 0x4000
+ 
+ #define SYNTHVID_VERSION(major, minor) ((minor) << 16 | (major))
+ #define SYNTHVID_VERSION_WIN7 SYNTHVID_VERSION(3, 0)
+ #define SYNTHVID_VERSION_WIN8 SYNTHVID_VERSION(3, 2)
+ 
+ #define SYNTHVID_DEPTH_WIN7 16
+ #define SYNTHVID_DEPTH_WIN8 32
+ 
+ #define SYNTHVID_FB_SIZE_WIN7 (4 * 1024 * 1024)
+ #define SYNTHVID_WIDTH_MAX_WIN7 1600
+ #define SYNTHVID_HEIGHT_MAX_WIN7 1200
+ 
+ #define SYNTHVID_FB_SIZE_WIN8 (8 * 1024 * 1024)
+ 
+ #define PCI_VENDOR_ID_MICROSOFT 0x1414
+ #define PCI_DEVICE_ID_HYPERV_VIDEO 0x5353
+ 
+ 
+ enum pipe_msg_type {
+ 	PIPE_MSG_INVALID,
+ 	PIPE_MSG_DATA,
+ 	PIPE_MSG_MAX
+ };
+ 
+ struct pipe_msg_hdr {
+ 	u32 type;
+ 	u32 size; /* size of message after this field */
+ } __packed;
+ 
+ 
+ enum synthvid_msg_type {
+ 	SYNTHVID_ERROR			= 0,
+ 	SYNTHVID_VERSION_REQUEST	= 1,
+ 	SYNTHVID_VERSION_RESPONSE	= 2,
+ 	SYNTHVID_VRAM_LOCATION		= 3,
+ 	SYNTHVID_VRAM_LOCATION_ACK	= 4,
+ 	SYNTHVID_SITUATION_UPDATE	= 5,
+ 	SYNTHVID_SITUATION_UPDATE_ACK	= 6,
+ 	SYNTHVID_POINTER_POSITION	= 7,
+ 	SYNTHVID_POINTER_SHAPE		= 8,
+ 	SYNTHVID_FEATURE_CHANGE		= 9,
+ 	SYNTHVID_DIRT			= 10,
+ 
+ 	SYNTHVID_MAX			= 11
+ };
+ 
+ struct synthvid_msg_hdr {
+ 	u32 type;
+ 	u32 size;  /* size of this header + payload after this field*/
+ } __packed;
+ 
+ 
+ struct synthvid_version_req {
+ 	u32 version;
+ } __packed;
+ 
+ struct synthvid_version_resp {
+ 	u32 version;
+ 	u8 is_accepted;
+ 	u8 max_video_outputs;
+ } __packed;
+ 
+ struct synthvid_vram_location {
+ 	u64 user_ctx;
+ 	u8 is_vram_gpa_specified;
+ 	u64 vram_gpa;
+ } __packed;
+ 
+ struct synthvid_vram_location_ack {
+ 	u64 user_ctx;
+ } __packed;
+ 
+ struct video_output_situation {
+ 	u8 active;
+ 	u32 vram_offset;
+ 	u8 depth_bits;
+ 	u32 width_pixels;
+ 	u32 height_pixels;
+ 	u32 pitch_bytes;
+ } __packed;
+ 
+ struct synthvid_situation_update {
+ 	u64 user_ctx;
+ 	u8 video_output_count;
+ 	struct video_output_situation video_output[1];
+ } __packed;
+ 
+ struct synthvid_situation_update_ack {
+ 	u64 user_ctx;
+ } __packed;
+ 
+ struct synthvid_pointer_position {
+ 	u8 is_visible;
+ 	u8 video_output;
+ 	s32 image_x;
+ 	s32 image_y;
+ } __packed;
+ 
+ 
+ #define CURSOR_MAX_X 96
+ #define CURSOR_MAX_Y 96
+ #define CURSOR_ARGB_PIXEL_SIZE 4
+ #define CURSOR_MAX_SIZE (CURSOR_MAX_X * CURSOR_MAX_Y * CURSOR_ARGB_PIXEL_SIZE)
+ #define CURSOR_COMPLETE (-1)
+ 
+ struct synthvid_pointer_shape {
+ 	u8 part_idx;
+ 	u8 is_argb;
+ 	u32 width; /* CURSOR_MAX_X at most */
+ 	u32 height; /* CURSOR_MAX_Y at most */
+ 	u32 hot_x; /* hotspot relative to upper-left of pointer image */
+ 	u32 hot_y;
+ 	u8 data[4];
+ } __packed;
+ 
+ struct synthvid_feature_change {
+ 	u8 is_dirt_needed;
+ 	u8 is_ptr_pos_needed;
+ 	u8 is_ptr_shape_needed;
+ 	u8 is_situ_needed;
+ } __packed;
+ 
+ struct rect {
+ 	s32 x1, y1; /* top left corner */
+ 	s32 x2, y2; /* bottom right corner, exclusive */
+ } __packed;
+ 
+ struct synthvid_dirt {
+ 	u8 video_output;
+ 	u8 dirt_count;
+ 	struct rect rect[1];
+ } __packed;
+ 
+ struct synthvid_msg {
+ 	struct pipe_msg_hdr pipe_hdr;
+ 	struct synthvid_msg_hdr vid_hdr;
+ 	union {
+ 		struct synthvid_version_req ver_req;
+ 		struct synthvid_version_resp ver_resp;
+ 		struct synthvid_vram_location vram;
+ 		struct synthvid_vram_location_ack vram_ack;
+ 		struct synthvid_situation_update situ;
+ 		struct synthvid_situation_update_ack situ_ack;
+ 		struct synthvid_pointer_position ptr_pos;
+ 		struct synthvid_pointer_shape ptr_shape;
+ 		struct synthvid_feature_change feature_chg;
+ 		struct synthvid_dirt dirt;
+ 	};
+ } __packed;
+ 
+ 
+ 
+ /* FB driver definitions and structures */
+ #define HVFB_WIDTH 1152 /* default screen width */
+ #define HVFB_HEIGHT 864 /* default screen height */
+ #define HVFB_WIDTH_MIN 640
+ #define HVFB_HEIGHT_MIN 480
+ 
+ #define RING_BUFSIZE (256 * 1024)
+ #define VSP_TIMEOUT (10 * HZ)
+ #define HVFB_UPDATE_DELAY (HZ / 20)
+ 
+ struct hvfb_par {
+ 	struct fb_info *info;
+ 	bool fb_ready; /* fb device is ready */
+ 	struct completion wait;
+ 	u32 synthvid_version;
+ 
+ 	struct delayed_work dwork;
+ 	bool update;
+ 
+ 	u32 pseudo_palette[16];
+ 	u8 init_buf[MAX_VMBUS_PKT_SIZE];
+ 	u8 recv_buf[MAX_VMBUS_PKT_SIZE];
+ };
+ 
+ static uint screen_width = HVFB_WIDTH;
+ static uint screen_height = HVFB_HEIGHT;
+ static uint screen_depth;
+ static uint screen_fb_size;
+ 
+ /* Send message to Hyper-V host */
+ static inline int synthvid_send(struct hv_device *hdev,
+ 				struct synthvid_msg *msg)
+ {
+ 	static atomic64_t request_id = ATOMIC64_INIT(0);
+ 	int ret;
+ 
+ 	msg->pipe_hdr.type = PIPE_MSG_DATA;
+ 	msg->pipe_hdr.size = msg->vid_hdr.size;
+ 
+ 	ret = vmbus_sendpacket(hdev->channel, msg,
+ 			       msg->vid_hdr.size + sizeof(struct pipe_msg_hdr),
+ 			       atomic64_inc_return(&request_id),
+ 			       VM_PKT_DATA_INBAND, 0);
+ 
+ 	if (ret)
+ 		pr_err("Unable to send packet via vmbus\n");
+ 
+ 	return ret;
+ }
+ 
+ 
+ /* Send screen resolution info to host */
+ static int synthvid_send_situ(struct hv_device *hdev)
+ {
+ 	struct fb_info *info = hv_get_drvdata(hdev);
+ 	struct synthvid_msg msg;
+ 
+ 	if (!info)
+ 		return -ENODEV;
+ 
+ 	memset(&msg, 0, sizeof(struct synthvid_msg));
+ 
+ 	msg.vid_hdr.type = SYNTHVID_SITUATION_UPDATE;
+ 	msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
+ 		sizeof(struct synthvid_situation_update);
+ 	msg.situ.user_ctx = 0;
+ 	msg.situ.video_output_count = 1;
+ 	msg.situ.video_output[0].active = 1;
+ 	msg.situ.video_output[0].vram_offset = 0;
+ 	msg.situ.video_output[0].depth_bits = info->var.bits_per_pixel;
+ 	msg.situ.video_output[0].width_pixels = info->var.xres;
+ 	msg.situ.video_output[0].height_pixels = info->var.yres;
+ 	msg.situ.video_output[0].pitch_bytes = info->fix.line_length;
+ 
+ 	synthvid_send(hdev, &msg);
+ 
+ 	return 0;
+ }
+ 
+ /* Send mouse pointer info to host */
+ static int synthvid_send_ptr(struct hv_device *hdev)
+ {
+ 	struct synthvid_msg msg;
+ 
+ 	memset(&msg, 0, sizeof(struct synthvid_msg));
+ 	msg.vid_hdr.type = SYNTHVID_POINTER_POSITION;
+ 	msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
+ 		sizeof(struct synthvid_pointer_position);
+ 	msg.ptr_pos.is_visible = 1;
+ 	msg.ptr_pos.video_output = 0;
+ 	msg.ptr_pos.image_x = 0;
+ 	msg.ptr_pos.image_y = 0;
+ 	synthvid_send(hdev, &msg);
+ 
+ 	memset(&msg, 0, sizeof(struct synthvid_msg));
+ 	msg.vid_hdr.type = SYNTHVID_POINTER_SHAPE;
+ 	msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
+ 		sizeof(struct synthvid_pointer_shape);
+ 	msg.ptr_shape.part_idx = CURSOR_COMPLETE;
+ 	msg.ptr_shape.is_argb = 1;
+ 	msg.ptr_shape.width = 1;
+ 	msg.ptr_shape.height = 1;
+ 	msg.ptr_shape.hot_x = 0;
+ 	msg.ptr_shape.hot_y = 0;
+ 	msg.ptr_shape.data[0] = 0;
+ 	msg.ptr_shape.data[1] = 1;
+ 	msg.ptr_shape.data[2] = 1;
+ 	msg.ptr_shape.data[3] = 1;
+ 	synthvid_send(hdev, &msg);
+ 
+ 	return 0;
+ }
+ 
+ /* Send updated screen area (dirty rectangle) location to host */
+ static int synthvid_update(struct fb_info *info)
+ {
+ 	struct hv_device *hdev = device_to_hv_device(info->device);
+ 	struct synthvid_msg msg;
+ 
+ 	memset(&msg, 0, sizeof(struct synthvid_msg));
+ 
+ 	msg.vid_hdr.type = SYNTHVID_DIRT;
+ 	msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
+ 		sizeof(struct synthvid_dirt);
+ 	msg.dirt.video_output = 0;
+ 	msg.dirt.dirt_count = 1;
+ 	msg.dirt.rect[0].x1 = 0;
+ 	msg.dirt.rect[0].y1 = 0;
+ 	msg.dirt.rect[0].x2 = info->var.xres;
+ 	msg.dirt.rect[0].y2 = info->var.yres;
+ 
+ 	synthvid_send(hdev, &msg);
+ 
+ 	return 0;
+ }
+ 
+ 
+ /*
+  * Actions on received messages from host:
+  * Complete the wait event.
+  * Or, reply with screen and cursor info.
+  */
+ static void synthvid_recv_sub(struct hv_device *hdev)
+ {
+ 	struct fb_info *info = hv_get_drvdata(hdev);
+ 	struct hvfb_par *par;
+ 	struct synthvid_msg *msg;
+ 
+ 	if (!info)
+ 		return;
+ 
+ 	par = info->par;
+ 	msg = (struct synthvid_msg *)par->recv_buf;
+ 
+ 	/* Complete the wait event */
+ 	if (msg->vid_hdr.type == SYNTHVID_VERSION_RESPONSE ||
+ 	    msg->vid_hdr.type == SYNTHVID_VRAM_LOCATION_ACK) {
+ 		memcpy(par->init_buf, msg, MAX_VMBUS_PKT_SIZE);
+ 		complete(&par->wait);
+ 		return;
+ 	}
+ 
+ 	/* Reply with screen and cursor info */
+ 	if (msg->vid_hdr.type == SYNTHVID_FEATURE_CHANGE) {
+ 		if (par->fb_ready) {
+ 			synthvid_send_ptr(hdev);
+ 			synthvid_send_situ(hdev);
+ 		}
+ 
+ 		par->update = msg->feature_chg.is_dirt_needed;
+ 		if (par->update)
+ 			schedule_delayed_work(&par->dwork, HVFB_UPDATE_DELAY);
+ 	}
+ }
+ 
+ /* Receive callback for messages from the host */
+ static void synthvid_receive(void *ctx)
+ {
+ 	struct hv_device *hdev = ctx;
+ 	struct fb_info *info = hv_get_drvdata(hdev);
+ 	struct hvfb_par *par;
+ 	struct synthvid_msg *recv_buf;
+ 	u32 bytes_recvd;
+ 	u64 req_id;
+ 	int ret;
+ 
+ 	if (!info)
+ 		return;
+ 
+ 	par = info->par;
+ 	recv_buf = (struct synthvid_msg *)par->recv_buf;
+ 
+ 	do {
+ 		ret = vmbus_recvpacket(hdev->channel, recv_buf,
+ 				       MAX_VMBUS_PKT_SIZE,
+ 				       &bytes_recvd, &req_id);
+ 		if (bytes_recvd > 0 &&
+ 		    recv_buf->pipe_hdr.type == PIPE_MSG_DATA)
+ 			synthvid_recv_sub(hdev);
+ 	} while (bytes_recvd > 0 && ret == 0);
+ }
+ 
+ /* Check synthetic video protocol version with the host */
+ static int synthvid_negotiate_ver(struct hv_device *hdev, u32 ver)
+ {
+ 	struct fb_info *info = hv_get_drvdata(hdev);
+ 	struct hvfb_par *par = info->par;
+ 	struct synthvid_msg *msg = (struct synthvid_msg *)par->init_buf;
+ 	int t, ret = 0;
+ 
+ 	memset(msg, 0, sizeof(struct synthvid_msg));
+ 	msg->vid_hdr.type = SYNTHVID_VERSION_REQUEST;
+ 	msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
+ 		sizeof(struct synthvid_version_req);
+ 	msg->ver_req.version = ver;
+ 	synthvid_send(hdev, msg);
+ 
+ 	t = wait_for_completion_timeout(&par->wait, VSP_TIMEOUT);
+ 	if (!t) {
+ 		pr_err("Time out on waiting version response\n");
+ 		ret = -ETIMEDOUT;
+ 		goto out;
+ 	}
+ 	if (!msg->ver_resp.is_accepted) {
+ 		ret = -ENODEV;
+ 		goto out;
+ 	}
+ 
+ 	par->synthvid_version = ver;
+ 
+ out:
+ 	return ret;
+ }
+ 
+ /* Connect to VSP (Virtual Service Provider) on host */
+ static int synthvid_connect_vsp(struct hv_device *hdev)
+ {
+ 	struct fb_info *info = hv_get_drvdata(hdev);
+ 	struct hvfb_par *par = info->par;
+ 	int ret;
+ 
+ 	ret = vmbus_open(hdev->channel, RING_BUFSIZE, RING_BUFSIZE,
+ 			 NULL, 0, synthvid_receive, hdev);
+ 	if (ret) {
+ 		pr_err("Unable to open vmbus channel\n");
+ 		return ret;
+ 	}
+ 
+ 	/* Negotiate the protocol version with host */
+ 	if (vmbus_proto_version == VERSION_WS2008 ||
+ 	    vmbus_proto_version == VERSION_WIN7)
+ 		ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN7);
+ 	else
+ 		ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN8);
+ 
+ 	if (ret) {
+ 		pr_err("Synthetic video device version not accepted\n");
+ 		goto error;
+ 	}
+ 
+ 	if (par->synthvid_version == SYNTHVID_VERSION_WIN7) {
+ 		screen_depth = SYNTHVID_DEPTH_WIN7;
+ 		screen_fb_size = SYNTHVID_FB_SIZE_WIN7;
+ 	} else {
+ 		screen_depth = SYNTHVID_DEPTH_WIN8;
+ 		screen_fb_size = SYNTHVID_FB_SIZE_WIN8;
+ 	}
+ 
+ 	return 0;
+ 
+ error:
+ 	vmbus_close(hdev->channel);
+ 	return ret;
+ }
+ 
+ /* Send VRAM and Situation messages to the host */
+ static int synthvid_send_config(struct hv_device *hdev)
+ {
+ 	struct fb_info *info = hv_get_drvdata(hdev);
+ 	struct hvfb_par *par = info->par;
+ 	struct synthvid_msg *msg = (struct synthvid_msg *)par->init_buf;
+ 	int t, ret = 0;
+ 
+ 	/* Send VRAM location */
+ 	memset(msg, 0, sizeof(struct synthvid_msg));
+ 	msg->vid_hdr.type = SYNTHVID_VRAM_LOCATION;
+ 	msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
+ 		sizeof(struct synthvid_vram_location);
+ 	msg->vram.user_ctx = msg->vram.vram_gpa = info->fix.smem_start;
+ 	msg->vram.is_vram_gpa_specified = 1;
+ 	synthvid_send(hdev, msg);
+ 
+ 	t = wait_for_completion_timeout(&par->wait, VSP_TIMEOUT);
+ 	if (!t) {
+ 		pr_err("Time out on waiting vram location ack\n");
+ 		ret = -ETIMEDOUT;
+ 		goto out;
+ 	}
+ 	if (msg->vram_ack.user_ctx != info->fix.smem_start) {
+ 		pr_err("Unable to set VRAM location\n");
+ 		ret = -ENODEV;
+ 		goto out;
+ 	}
+ 
+ 	/* Send pointer and situation update */
+ 	synthvid_send_ptr(hdev);
+ 	synthvid_send_situ(hdev);
+ 
+ out:
+ 	return ret;
+ }
+ 
+ 
+ /*
+  * Delayed work callback:
+  * It is called at HVFB_UPDATE_DELAY or longer time interval to process
+  * screen updates. It is re-scheduled if further update is necessary.
+  */
+ static void hvfb_update_work(struct work_struct *w)
+ {
+ 	struct hvfb_par *par = container_of(w, struct hvfb_par, dwork.work);
+ 	struct fb_info *info = par->info;
+ 
+ 	if (par->fb_ready)
+ 		synthvid_update(info);
+ 
+ 	if (par->update)
+ 		schedule_delayed_work(&par->dwork, HVFB_UPDATE_DELAY);
+ }
+ 
+ 
+ /* Framebuffer operation handlers */
+ 
+ static int hvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+ {
+ 	if (var->xres < HVFB_WIDTH_MIN || var->yres < HVFB_HEIGHT_MIN ||
+ 	    var->xres > screen_width || var->yres >  screen_height ||
+ 	    var->bits_per_pixel != screen_depth)
+ 		return -EINVAL;
+ 
+ 	var->xres_virtual = var->xres;
+ 	var->yres_virtual = var->yres;
+ 
+ 	return 0;
+ }
+ 
+ static int hvfb_set_par(struct fb_info *info)
+ {
+ 	struct hv_device *hdev = device_to_hv_device(info->device);
+ 
+ 	return synthvid_send_situ(hdev);
+ }
+ 
+ 
+ static inline u32 chan_to_field(u32 chan, struct fb_bitfield *bf)
+ {
+ 	return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
+ }
+ 
+ static int hvfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ 			  unsigned blue, unsigned transp, struct fb_info *info)
+ {
+ 	u32 *pal = info->pseudo_palette;
+ 
+ 	if (regno > 15)
+ 		return -EINVAL;
+ 
+ 	pal[regno] = chan_to_field(red, &info->var.red)
+ 		| chan_to_field(green, &info->var.green)
+ 		| chan_to_field(blue, &info->var.blue)
+ 		| chan_to_field(transp, &info->var.transp);
+ 
+ 	return 0;
+ }
+ 
+ 
+ static struct fb_ops hvfb_ops = {
+ 	.owner = THIS_MODULE,
+ 	.fb_check_var = hvfb_check_var,
+ 	.fb_set_par = hvfb_set_par,
+ 	.fb_setcolreg = hvfb_setcolreg,
+ 	.fb_fillrect = cfb_fillrect,
+ 	.fb_copyarea = cfb_copyarea,
+ 	.fb_imageblit = cfb_imageblit,
+ };
+ 
+ 
+ /* Get options from kernel paramenter "video=" */
+ static void hvfb_get_option(struct fb_info *info)
+ {
+ 	struct hvfb_par *par = info->par;
+ 	char *opt = NULL, *p;
+ 	uint x = 0, y = 0;
+ 
+ 	if (fb_get_options(KBUILD_MODNAME, &opt) || !opt || !*opt)
+ 		return;
+ 
+ 	p = strsep(&opt, "x");
+ 	if (!*p || kstrtouint(p, 0, &x) ||
+ 	    !opt || !*opt || kstrtouint(opt, 0, &y)) {
+ 		pr_err("Screen option is invalid: skipped\n");
+ 		return;
+ 	}
+ 
+ 	if (x < HVFB_WIDTH_MIN || y < HVFB_HEIGHT_MIN ||
+ 	    (par->synthvid_version == SYNTHVID_VERSION_WIN8 &&
+ 	     x * y * screen_depth / 8 > SYNTHVID_FB_SIZE_WIN8) ||
+ 	    (par->synthvid_version == SYNTHVID_VERSION_WIN7 &&
+ 	     (x > SYNTHVID_WIDTH_MAX_WIN7 || y > SYNTHVID_HEIGHT_MAX_WIN7))) {
+ 		pr_err("Screen resolution option is out of range: skipped\n");
+ 		return;
+ 	}
+ 
+ 	screen_width = x;
+ 	screen_height = y;
+ 	return;
+ }
+ 
+ 
+ /* Get framebuffer memory from Hyper-V video pci space */
+ static int hvfb_getmem(struct fb_info *info)
+ {
+ 	struct pci_dev *pdev;
+ 	ulong fb_phys;
+ 	void *fb_virt;
+ 
+ 	pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT,
+ 			      PCI_DEVICE_ID_HYPERV_VIDEO, NULL);
+ 	if (!pdev) {
+ 		pr_err("Unable to find PCI Hyper-V video\n");
+ 		return -ENODEV;
+ 	}
+ 
+ 	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) ||
+ 	    pci_resource_len(pdev, 0) < screen_fb_size)
+ 		goto err1;
+ 
+ 	fb_phys = pci_resource_end(pdev, 0) - screen_fb_size + 1;
+ 	if (!request_mem_region(fb_phys, screen_fb_size, KBUILD_MODNAME))
+ 		goto err1;
+ 
+ 	fb_virt = ioremap(fb_phys, screen_fb_size);
+ 	if (!fb_virt)
+ 		goto err2;
+ 
+ 	info->apertures = alloc_apertures(1);
+ 	if (!info->apertures)
+ 		goto err3;
+ 
+ 	info->apertures->ranges[0].base = pci_resource_start(pdev, 0);
+ 	info->apertures->ranges[0].size = pci_resource_len(pdev, 0);
+ 	info->fix.smem_start = fb_phys;
+ 	info->fix.smem_len = screen_fb_size;
+ 	info->screen_base = fb_virt;
+ 	info->screen_size = screen_fb_size;
+ 
+ 	pci_dev_put(pdev);
+ 	return 0;
+ 
+ err3:
+ 	iounmap(fb_virt);
+ err2:
+ 	release_mem_region(fb_phys, screen_fb_size);
+ err1:
+ 	pci_dev_put(pdev);
+ 	return -ENOMEM;
+ }
+ 
+ /* Release the framebuffer */
+ static void hvfb_putmem(struct fb_info *info)
+ {
+ 	iounmap(info->screen_base);
+ 	release_mem_region(info->fix.smem_start, screen_fb_size);
+ }
+ 
+ 
+ static int hvfb_probe(struct hv_device *hdev,
+ 		      const struct hv_vmbus_device_id *dev_id)
+ {
+ 	struct fb_info *info;
+ 	struct hvfb_par *par;
+ 	int ret;
+ 
+ 	info = framebuffer_alloc(sizeof(struct hvfb_par), &hdev->device);
+ 	if (!info) {
+ 		pr_err("No memory for framebuffer info\n");
+ 		return -ENOMEM;
+ 	}
+ 
+ 	par = info->par;
+ 	par->info = info;
+ 	par->fb_ready = false;
+ 	init_completion(&par->wait);
+ 	INIT_DELAYED_WORK(&par->dwork, hvfb_update_work);
+ 
+ 	/* Connect to VSP */
+ 	hv_set_drvdata(hdev, info);
+ 	ret = synthvid_connect_vsp(hdev);
+ 	if (ret) {
+ 		pr_err("Unable to connect to VSP\n");
+ 		goto error1;
+ 	}
+ 
+ 	ret = hvfb_getmem(info);
+ 	if (ret) {
+ 		pr_err("No memory for framebuffer\n");
+ 		goto error2;
+ 	}
+ 
+ 	hvfb_get_option(info);
+ 	pr_info("Screen resolution: %dx%d, Color depth: %d\n",
+ 		screen_width, screen_height, screen_depth);
+ 
+ 
+ 	/* Set up fb_info */
+ 	info->flags = FBINFO_DEFAULT;
+ 
+ 	info->var.xres_virtual = info->var.xres = screen_width;
+ 	info->var.yres_virtual = info->var.yres = screen_height;
+ 	info->var.bits_per_pixel = screen_depth;
+ 
+ 	if (info->var.bits_per_pixel == 16) {
+ 		info->var.red = (struct fb_bitfield){11, 5, 0};
+ 		info->var.green = (struct fb_bitfield){5, 6, 0};
+ 		info->var.blue = (struct fb_bitfield){0, 5, 0};
+ 		info->var.transp = (struct fb_bitfield){0, 0, 0};
+ 	} else {
+ 		info->var.red = (struct fb_bitfield){16, 8, 0};
+ 		info->var.green = (struct fb_bitfield){8, 8, 0};
+ 		info->var.blue = (struct fb_bitfield){0, 8, 0};
+ 		info->var.transp = (struct fb_bitfield){24, 8, 0};
+ 	}
+ 
+ 	info->var.activate = FB_ACTIVATE_NOW;
+ 	info->var.height = -1;
+ 	info->var.width = -1;
+ 	info->var.vmode = FB_VMODE_NONINTERLACED;
+ 
+ 	strcpy(info->fix.id, KBUILD_MODNAME);
+ 	info->fix.type = FB_TYPE_PACKED_PIXELS;
+ 	info->fix.visual = FB_VISUAL_TRUECOLOR;
+ 	info->fix.line_length = screen_width * screen_depth / 8;
+ 	info->fix.accel = FB_ACCEL_NONE;
+ 
+ 	info->fbops = &hvfb_ops;
+ 	info->pseudo_palette = par->pseudo_palette;
+ 
+ 	/* Send config to host */
+ 	ret = synthvid_send_config(hdev);
+ 	if (ret)
+ 		goto error;
+ 
+ 	ret = register_framebuffer(info);
+ 	if (ret) {
+ 		pr_err("Unable to register framebuffer\n");
+ 		goto error;
+ 	}
+ 
+ 	par->fb_ready = true;
+ 
+ 	return 0;
+ 
+ error:
+ 	hvfb_putmem(info);
+ error2:
+ 	vmbus_close(hdev->channel);
+ error1:
+ 	cancel_delayed_work_sync(&par->dwork);
+ 	hv_set_drvdata(hdev, NULL);
+ 	framebuffer_release(info);
+ 	return ret;
+ }
+ 
+ 
+ static int hvfb_remove(struct hv_device *hdev)
+ {
+ 	struct fb_info *info = hv_get_drvdata(hdev);
+ 	struct hvfb_par *par = info->par;
+ 
+ 	par->update = false;
+ 	par->fb_ready = false;
+ 
+ 	unregister_framebuffer(info);
+ 	cancel_delayed_work_sync(&par->dwork);
+ 
+ 	vmbus_close(hdev->channel);
+ 	hv_set_drvdata(hdev, NULL);
+ 
+ 	hvfb_putmem(info);
+ 	framebuffer_release(info);
+ 
+ 	return 0;
+ }
+ 
+ 
+ static const struct hv_vmbus_device_id id_table[] = {
+ 	/* Synthetic Video Device GUID */
+ 	{HV_SYNTHVID_GUID},
+ 	{}
+ };
+ 
+ MODULE_DEVICE_TABLE(vmbus, id_table);
+ 
+ static struct hv_driver hvfb_drv = {
+ 	.name = KBUILD_MODNAME,
+ 	.id_table = id_table,
+ 	.probe = hvfb_probe,
+ 	.remove = hvfb_remove,
+ };
+ 
+ 
+ static int __init hvfb_drv_init(void)
+ {
+ 	return vmbus_driver_register(&hvfb_drv);
+ }
+ 
+ static void __exit hvfb_drv_exit(void)
+ {
+ 	vmbus_driver_unregister(&hvfb_drv);
+ }
+ 
+ module_init(hvfb_drv_init);
+ module_exit(hvfb_drv_exit);
+ 
+ MODULE_LICENSE("GPL");
+ MODULE_VERSION(HV_DRV_VERSION);
+ MODULE_DESCRIPTION("Microsoft Hyper-V Synthetic Video Frame Buffer Driver");
diff -crN linux-3.8.6-gentoo/drivers/xen/events.c linux-3.8.6-gentoo-hyperv/drivers/xen/events.c
*** linux-3.8.6-gentoo/drivers/xen/events.c	Sat Apr 13 02:03:29 2013
--- linux-3.8.6-gentoo-hyperv/drivers/xen/events.c	Sat Apr 13 02:24:38 2013
***************
*** 1797,1803 ****
  	int rc;
  	uint64_t callback_via;
  	if (xen_have_vector_callback) {
! 		callback_via = HVM_CALLBACK_VECTOR(XEN_HVM_EVTCHN_CALLBACK);
  		rc = xen_set_callback_via(callback_via);
  		if (rc) {
  			printk(KERN_ERR "Request for Xen HVM callback vector"
--- 1797,1803 ----
  	int rc;
  	uint64_t callback_via;
  	if (xen_have_vector_callback) {
! 		callback_via = HVM_CALLBACK_VECTOR(HYPERVISOR_CALLBACK_VECTOR);
  		rc = xen_set_callback_via(callback_via);
  		if (rc) {
  			printk(KERN_ERR "Request for Xen HVM callback vector"
***************
*** 1808,1815 ****
  		printk(KERN_INFO "Xen HVM callback vector for event delivery is "
  				"enabled\n");
  		/* in the restore case the vector has already been allocated */
! 		if (!test_bit(XEN_HVM_EVTCHN_CALLBACK, used_vectors))
! 			alloc_intr_gate(XEN_HVM_EVTCHN_CALLBACK, xen_hvm_callback_vector);
  	}
  }
  #else
--- 1808,1815 ----
  		printk(KERN_INFO "Xen HVM callback vector for event delivery is "
  				"enabled\n");
  		/* in the restore case the vector has already been allocated */
! 		if (!test_bit(HYPERVISOR_CALLBACK_VECTOR, used_vectors))
! 			alloc_intr_gate(HYPERVISOR_CALLBACK_VECTOR, xen_hvm_callback_vector);
  	}
  }
  #else
diff -crN linux-3.8.6-gentoo/include/linux/hyperv.h linux-3.8.6-gentoo-hyperv/include/linux/hyperv.h
*** linux-3.8.6-gentoo/include/linux/hyperv.h	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/include/linux/hyperv.h	Sat Apr 13 02:25:23 2013
***************
*** 27,32 ****
--- 27,89 ----
  
  #include <linux/types.h>
  
+ 
+ /*
+  * Implementation of host controlled snapshot of the guest.
+  */
+ 
+ #define VSS_OP_REGISTER 128
+ 
+ enum hv_vss_op {
+ 	VSS_OP_CREATE = 0,
+ 	VSS_OP_DELETE,
+ 	VSS_OP_HOT_BACKUP,
+ 	VSS_OP_GET_DM_INFO,
+ 	VSS_OP_BU_COMPLETE,
+ 	/*
+ 	 * Following operations are only supported with IC version >= 5.0
+ 	 */
+ 	VSS_OP_FREEZE, /* Freeze the file systems in the VM */
+ 	VSS_OP_THAW, /* Unfreeze the file systems */
+ 	VSS_OP_AUTO_RECOVER,
+ 	VSS_OP_COUNT /* Number of operations, must be last */
+ };
+ 
+ 
+ /*
+  * Header for all VSS messages.
+  */
+ struct hv_vss_hdr {
+ 	__u8 operation;
+ 	__u8 reserved[7];
+ } __attribute__((packed));
+ 
+ 
+ /*
+  * Flag values for the hv_vss_check_feature. Linux supports only
+  * one value.
+  */
+ #define VSS_HBU_NO_AUTO_RECOVERY	0x00000005
+ 
+ struct hv_vss_check_feature {
+ 	__u32 flags;
+ } __attribute__((packed));
+ 
+ struct hv_vss_check_dm_info {
+ 	__u32 flags;
+ } __attribute__((packed));
+ 
+ struct hv_vss_msg {
+ 	union {
+ 		struct hv_vss_hdr vss_hdr;
+ 		int error;
+ 	};
+ 	union {
+ 		struct hv_vss_check_feature vss_cf;
+ 		struct hv_vss_check_dm_info dm_info;
+ 	};
+ } __attribute__((packed));
+ 
  /*
   * An implementation of HyperV key value pair (KVP) functionality for Linux.
   *
***************
*** 325,338 ****
  
  	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
--- 382,409 ----
  
  	u32 interrupt_mask;
  
! 	/*
! 	 * Win8 uses some of the reserved bits to implement
! 	 * interrupt driven flow management. On the send side
! 	 * we can request that the receiver interrupt the sender
! 	 * when the ring transitions from being full to being able
! 	 * to handle a message of size "pending_send_sz".
! 	 *
! 	 * Add necessary state for this enhancement.
  	 */
+ 	u32 pending_send_sz;
+ 
+ 	u32 reserved1[12];
+ 
+ 	union {
+ 		struct {
+ 			u32 feat_pending_send_sz:1;
+ 		};
+ 		u32 value;
+ 	} feature_bits;
+ 
+ 	/* Pad it to PAGE_SIZE so that data starts on page boundary */
+ 	u8	reserved2[4028];
  
  	/*
  	 * Ring data starts here + RingDataStartOffset
***************
*** 405,416 ****
   */
  #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)
--- 476,497 ----
   */
  #define HV_DRV_VERSION           "3.1"
  
  /*
!  * VMBUS version is 32 bit entity broken up into
!  * two 16 bit quantities: major_number. minor_number.
!  *
!  * 0 . 13 (Windows Server 2008)
!  * 1 . 1  (Windows 7)
!  * 2 . 4  (Windows 8)
   */
! 
! #define VERSION_WS2008  ((0 << 16) | (13))
! #define VERSION_WIN7    ((1 << 16) | (1))
! #define VERSION_WIN8    ((2 << 16) | (4))
! 
! #define VERSION_INVAL -1
! 
! #define VERSION_CURRENT VERSION_WIN8
  
  /* Make maximum size of pipe payload of 16K */
  #define MAX_PIPE_DATA_PAYLOAD		(sizeof(u8) * 16384)
***************
*** 432,440 ****
  struct vmbus_channel_offer {
  	uuid_le if_type;
  	uuid_le 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 */
  
--- 513,525 ----
  struct vmbus_channel_offer {
  	uuid_le if_type;
  	uuid_le if_instance;
! 
! 	/*
! 	 * These two fields are not currently used.
! 	 */
! 	u64 reserved1;
! 	u64 reserved2;
! 
  	u16 chn_flags;
  	u16 mmio_megabytes;		/* in bytes * 1024 * 1024 */
  
***************
*** 456,462 ****
  			unsigned char user_def[MAX_PIPE_USER_DEFINED_BYTES];
  		} pipe;
  	} u;
! 	u32 padding;
  } __packed;
  
  /* Server Flags */
--- 541,551 ----
  			unsigned char user_def[MAX_PIPE_USER_DEFINED_BYTES];
  		} pipe;
  	} u;
! 	/*
! 	 * The sub_channel_index is defined in win8.
! 	 */
! 	u16 sub_channel_index;
! 	u16 reserved3;
  } __packed;
  
  /* Server Flags */
***************
*** 652,658 ****
  	struct vmbus_channel_offer offer;
  	u32 child_relid;
  	u8 monitorid;
! 	u8 monitor_allocated;
  } __packed;
  
  /* Rescind Offer parameters */
--- 741,765 ----
  	struct vmbus_channel_offer offer;
  	u32 child_relid;
  	u8 monitorid;
! 	/*
! 	 * win7 and beyond splits this field into a bit field.
! 	 */
! 	u8 monitor_allocated:1;
! 	u8 reserved:7;
! 	/*
! 	 * These are new fields added in win7 and later.
! 	 * Do not access these fields without checking the
! 	 * negotiated protocol.
! 	 *
! 	 * If "is_dedicated_interrupt" is set, we must not set the
! 	 * associated bit in the channel bitmap while sending the
! 	 * interrupt to the host.
! 	 *
! 	 * connection_id is to be used in signaling the host.
! 	 */
! 	u16 is_dedicated_interrupt:1;
! 	u16 reserved1:15;
! 	u32 connection_id;
  } __packed;
  
  /* Rescind Offer parameters */
***************
*** 683,690 ****
  	/* 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
--- 790,804 ----
  	/* GPADL for the channel's ring buffer. */
  	u32 ringbuffer_gpadlhandle;
  
! 	/*
! 	 * Starting with win8, this field will be used to specify
! 	 * the target virtual processor on which to deliver the interrupt for
! 	 * the host to guest communication.
! 	 * Prior to win8, incoming channel interrupts would only
! 	 * be delivered on cpu 0. Setting this value to 0 would
! 	 * preserve the earlier behavior.
! 	 */
! 	u32 target_vp;
  
  	/*
  	* The upstream ring buffer begins at offset zero in the memory
***************
*** 848,853 ****
--- 962,988 ----
  	struct vmbus_channel_close_channel msg;
  };
  
+ /* Define connection identifier type. */
+ union hv_connection_id {
+ 	u32 asu32;
+ 	struct {
+ 		u32 id:24;
+ 		u32 reserved:8;
+ 	} u;
+ };
+ 
+ /* Definition of the hv_signal_event hypercall input structure. */
+ struct hv_input_signal_event {
+ 	union hv_connection_id connectionid;
+ 	u16 flag_number;
+ 	u16 rsvdz;
+ };
+ 
+ struct hv_input_signal_event_buffer {
+ 	u64 align8;
+ 	struct hv_input_signal_event event;
+ };
+ 
  struct vmbus_channel {
  	struct list_head listentry;
  
***************
*** 882,889 ****
--- 1017,1058 ----
  
  	void (*onchannel_callback)(void *context);
  	void *channel_callback_context;
+ 
+ 	/*
+ 	 * A channel can be marked for efficient (batched)
+ 	 * reading:
+ 	 * If batched_reading is set to "true", we read until the
+ 	 * channel is empty and hold off interrupts from the host
+ 	 * during the entire read process.
+ 	 * If batched_reading is set to "false", the client is not
+ 	 * going to perform batched reading.
+ 	 *
+ 	 * By default we will enable batched reading; specific
+ 	 * drivers that don't want this behavior can turn it off.
+ 	 */
+ 
+ 	bool batched_reading;
+ 
+ 	bool is_dedicated_interrupt;
+ 	struct hv_input_signal_event_buffer sig_buf;
+ 	struct hv_input_signal_event *sig_event;
+ 
+ 	/*
+ 	 * Starting with win8, this field will be used to specify
+ 	 * the target virtual processor on which to deliver the interrupt for
+ 	 * the host to guest communication.
+ 	 * Prior to win8, incoming channel interrupts would only
+ 	 * be delivered on cpu 0. Setting this value to 0 would
+ 	 * preserve the earlier behavior.
+ 	 */
+ 	u32 target_vp;
  };
  
+ static inline void set_channel_read_state(struct vmbus_channel *c, bool state)
+ {
+ 	c->batched_reading = state;
+ }
+ 
  void vmbus_onmessage(void *context);
  
  int vmbus_request_offers(void);
***************
*** 1047,1052 ****
--- 1216,1335 ----
  		  g8, g9, ga, gb, gc, gd, ge, gf },
  
  /*
+  * GUID definitions of various offer types - services offered to the guest.
+  */
+ 
+ /*
+  * Network GUID
+  * {f8615163-df3e-46c5-913f-f2d2f965ed0e}
+  */
+ #define HV_NIC_GUID \
+ 	.guid = { \
+ 			0x63, 0x51, 0x61, 0xf8, 0x3e, 0xdf, 0xc5, 0x46, \
+ 			0x91, 0x3f, 0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e \
+ 		}
+ 
+ /*
+  * IDE GUID
+  * {32412632-86cb-44a2-9b5c-50d1417354f5}
+  */
+ #define HV_IDE_GUID \
+ 	.guid = { \
+ 			0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, \
+ 			0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5 \
+ 		}
+ 
+ /*
+  * SCSI GUID
+  * {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f}
+  */
+ #define HV_SCSI_GUID \
+ 	.guid = { \
+ 			0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, \
+ 			0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f \
+ 		}
+ 
+ /*
+  * Shutdown GUID
+  * {0e0b6031-5213-4934-818b-38d90ced39db}
+  */
+ #define HV_SHUTDOWN_GUID \
+ 	.guid = { \
+ 			0x31, 0x60, 0x0b, 0x0e, 0x13, 0x52, 0x34, 0x49, \
+ 			0x81, 0x8b, 0x38, 0xd9, 0x0c, 0xed, 0x39, 0xdb \
+ 		}
+ 
+ /*
+  * Time Synch GUID
+  * {9527E630-D0AE-497b-ADCE-E80AB0175CAF}
+  */
+ #define HV_TS_GUID \
+ 	.guid = { \
+ 			0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, \
+ 			0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf \
+ 		}
+ 
+ /*
+  * Heartbeat GUID
+  * {57164f39-9115-4e78-ab55-382f3bd5422d}
+  */
+ #define HV_HEART_BEAT_GUID \
+ 	.guid = { \
+ 			0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, \
+ 			0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d \
+ 		}
+ 
+ /*
+  * KVP GUID
+  * {a9a0f4e7-5a45-4d96-b827-8a841e8c03e6}
+  */
+ #define HV_KVP_GUID \
+ 	.guid = { \
+ 			0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, \
+ 			0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3,  0xe6 \
+ 		}
+ 
+ /*
+  * Dynamic memory GUID
+  * {525074dc-8985-46e2-8057-a307dc18a502}
+  */
+ #define HV_DM_GUID \
+ 	.guid = { \
+ 			0xdc, 0x74, 0x50, 0X52, 0x85, 0x89, 0xe2, 0x46, \
+ 			0x80, 0x57, 0xa3, 0x07, 0xdc, 0x18, 0xa5, 0x02 \
+ 		}
+ 
+ /*
+  * Mouse GUID
+  * {cfa8b69e-5b4a-4cc0-b98b-8ba1a1f3f95a}
+  */
+ #define HV_MOUSE_GUID \
+ 	.guid = { \
+ 			0x9e, 0xb6, 0xa8, 0xcf, 0x4a, 0x5b, 0xc0, 0x4c, \
+ 			0xb9, 0x8b, 0x8b, 0xa1, 0xa1, 0xf3, 0xf9, 0x5a \
+ 		}
+ 
+ /*
+  * VSS (Backup/Restore) GUID
+  */
+ #define HV_VSS_GUID \
+ 	.guid = { \
+ 			0x29, 0x2e, 0xfa, 0x35, 0x23, 0xea, 0x36, 0x42, \
+ 			0x96, 0xae, 0x3a, 0x6e, 0xba, 0xcb, 0xa4,  0x40 \
+ 		}
+ 
+ /*
+  * Synthetic Video GUID
+  * {DA0A7802-E377-4aac-8E77-0558EB1073F8}
+  */
+ #define HV_SYNTHVID_GUID \
+ 	.guid = { \
+ 			0x02, 0x78, 0x0a, 0xda, 0x77, 0xe3, 0xac, 0x4a, \
+ 			0x8e, 0x77, 0x05, 0x58, 0xeb, 0x10, 0x73, 0xf8 \
+ 		}
+ 
+ 
+ /*
   * Common header for Hyper-V ICs
   */
  
***************
*** 1150,1154 ****
--- 1433,1447 ----
  void hv_kvp_deinit(void);
  void hv_kvp_onchannelcallback(void *);
  
+ int hv_vss_init(struct hv_util_service *);
+ void hv_vss_deinit(void);
+ void hv_vss_onchannelcallback(void *);
+ 
+ /*
+  * Negotiated version with the Host.
+  */
+ 
+ extern __u32 vmbus_proto_version;
+ 
  #endif /* __KERNEL__ */
  #endif /* _HYPERV_H */
diff -crN linux-3.8.6-gentoo/include/linux/pci_ids.h linux-3.8.6-gentoo-hyperv/include/linux/pci_ids.h
*** linux-3.8.6-gentoo/include/linux/pci_ids.h	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/include/linux/pci_ids.h	Sat Apr 13 02:25:22 2013
***************
*** 2925,2927 ****
--- 2925,2930 ----
  #define PCI_DEVICE_ID_XEN_PLATFORM	0x0001
  
  #define PCI_VENDOR_ID_OCZ		0x1b85
+ 
+ #define PCI_VENDOR_ID_MICROSOFT		0x1414
+ #define PCI_DEVICE_ID_HYPERV_VIDEO	0x5353
diff -crN linux-3.8.6-gentoo/include/uapi/linux/connector.h linux-3.8.6-gentoo-hyperv/include/uapi/linux/connector.h
*** linux-3.8.6-gentoo/include/uapi/linux/connector.h	Tue Feb 19 08:58:34 2013
--- linux-3.8.6-gentoo-hyperv/include/uapi/linux/connector.h	Sat Apr 13 02:25:24 2013
***************
*** 44,51 ****
  #define CN_VAL_DRBD			0x1
  #define CN_KVP_IDX			0x9	/* HyperV KVP */
  #define CN_KVP_VAL			0x1	/* queries from the kernel */
  
! #define CN_NETLINK_USERS		10	/* Highest index + 1 */
  
  /*
   * Maximum connector's message size.
--- 44,53 ----
  #define CN_VAL_DRBD			0x1
  #define CN_KVP_IDX			0x9	/* HyperV KVP */
  #define CN_KVP_VAL			0x1	/* queries from the kernel */
+ #define CN_VSS_IDX			0xA	/* HyperV VSS */
+ #define CN_VSS_VAL			0x1	/* queries from the kernel */
  
! #define CN_NETLINK_USERS		11	/* Highest index + 1 */
  
  /*
   * Maximum connector's message size.
