diff mbox series

[PATHC,v6] video: hyperv: hyperv_fb: Support deferred IO for Hyper-V frame buffer driver

Message ID 20190918060227.6834-1-weh@microsoft.com (mailing list archive)
State New, archived
Headers show
Series [PATHC,v6] video: hyperv: hyperv_fb: Support deferred IO for Hyper-V frame buffer driver | expand

Commit Message

Wei Hu Sept. 18, 2019, 6:03 a.m. UTC
Without deferred IO support, hyperv_fb driver informs the host to refresh
the entire guest frame buffer at fixed rate, e.g. at 20Hz, no matter there
is screen update or not. This patch supports deferred IO for screens in
graphics mode and also enables the frame buffer on-demand refresh. The
highest refresh rate is still set at 20Hz.

Currently Hyper-V only takes a physical address from guest as the starting
address of frame buffer. This implies the guest must allocate contiguous
physical memory for frame buffer. In addition, Hyper-V Gen 2 VMs only
accept address from MMIO region as frame buffer address. Due to these
limitations on Hyper-V host, we keep a shadow copy of frame buffer
in the guest. This means one more copy of the dirty rectangle inside
guest when doing the on-demand refresh. This can be optimized in the
future with help from host. For now the host performance gain from deferred
IO outweighs the shadow copy impact in the guest.

Signed-off-by: Wei Hu <weh@microsoft.com>
---
    v2: Incorporated review comments from Michael Kelley
    - Increased dirty rectangle by one row in deferred IO case when sending
    to Hyper-V.
    - Corrected the dirty rectangle size in the text mode.
    - Added more comments.
    - Other minor code cleanups.

    v3: Incorporated more review comments
    - Removed a few unnecessary variable tests

    v4: Incorporated test and review feedback from Dexuan Cui
    - Not disable interrupt while acquiring docopy_lock in
      hvfb_update_work(). This avoids significant bootup delay in
      large vCPU count VMs.

    v5: Completely remove the unnecessary docopy_lock after discussing
    with Dexuan Cui.

    v6: Do not request host refresh when the VM guest screen is
    closed or minimized.

 drivers/video/fbdev/Kconfig     |   1 +
 drivers/video/fbdev/hyperv_fb.c | 210 ++++++++++++++++++++++++++++----
 2 files changed, 190 insertions(+), 21 deletions(-)

Comments

Dexuan Cui Sept. 18, 2019, 4:45 p.m. UTC | #1
> From: Wei Hu <weh@microsoft.com>
> Sent: Tuesday, September 17, 2019 11:03 PM
> [...]
> ---
>     v6: Do not request host refresh when the VM guest screen is
>     closed or minimized.

Looks good to me. Thanks!

Reviewed-by: Dexuan Cui <decui@microsoft.com>
Michael Kelley (LINUX) Sept. 18, 2019, 9:48 p.m. UTC | #2
From: Wei Hu <weh@microsoft.com> Sent: Tuesday, September 17, 2019 11:03 PM

> 
> Without deferred IO support, hyperv_fb driver informs the host to refresh
> the entire guest frame buffer at fixed rate, e.g. at 20Hz, no matter there
> is screen update or not. This patch supports deferred IO for screens in
> graphics mode and also enables the frame buffer on-demand refresh. The
> highest refresh rate is still set at 20Hz.
> 
> Currently Hyper-V only takes a physical address from guest as the starting
> address of frame buffer. This implies the guest must allocate contiguous
> physical memory for frame buffer. In addition, Hyper-V Gen 2 VMs only
> accept address from MMIO region as frame buffer address. Due to these
> limitations on Hyper-V host, we keep a shadow copy of frame buffer
> in the guest. This means one more copy of the dirty rectangle inside
> guest when doing the on-demand refresh. This can be optimized in the
> future with help from host. For now the host performance gain from deferred
> IO outweighs the shadow copy impact in the guest.
> 
> Signed-off-by: Wei Hu <weh@microsoft.com>
> ---
>     v2: Incorporated review comments from Michael Kelley
>     - Increased dirty rectangle by one row in deferred IO case when sending
>     to Hyper-V.
>     - Corrected the dirty rectangle size in the text mode.
>     - Added more comments.
>     - Other minor code cleanups.
> 
>     v3: Incorporated more review comments
>     - Removed a few unnecessary variable tests
> 
>     v4: Incorporated test and review feedback from Dexuan Cui
>     - Not disable interrupt while acquiring docopy_lock in
>       hvfb_update_work(). This avoids significant bootup delay in
>       large vCPU count VMs.
> 
>     v5: Completely remove the unnecessary docopy_lock after discussing
>     with Dexuan Cui.
> 
>     v6: Do not request host refresh when the VM guest screen is
>     closed or minimized.
> 
>  drivers/video/fbdev/Kconfig     |   1 +
>  drivers/video/fbdev/hyperv_fb.c | 210 ++++++++++++++++++++++++++++----
>  2 files changed, 190 insertions(+), 21 deletions(-)
> 

Reviewed-by: Michael Kelley <mikelley@microsoft.com>
Michael Kelley (LINUX) Sept. 20, 2019, 5:26 p.m. UTC | #3
From: Michael Kelley <mikelley@microsoft.com>  Sent: Wednesday, September 18, 2019 2:48 PM
> >
> > Without deferred IO support, hyperv_fb driver informs the host to refresh
> > the entire guest frame buffer at fixed rate, e.g. at 20Hz, no matter there
> > is screen update or not. This patch supports deferred IO for screens in
> > graphics mode and also enables the frame buffer on-demand refresh. The
> > highest refresh rate is still set at 20Hz.
> >
> > Currently Hyper-V only takes a physical address from guest as the starting
> > address of frame buffer. This implies the guest must allocate contiguous
> > physical memory for frame buffer. In addition, Hyper-V Gen 2 VMs only
> > accept address from MMIO region as frame buffer address. Due to these
> > limitations on Hyper-V host, we keep a shadow copy of frame buffer
> > in the guest. This means one more copy of the dirty rectangle inside
> > guest when doing the on-demand refresh. This can be optimized in the
> > future with help from host. For now the host performance gain from deferred
> > IO outweighs the shadow copy impact in the guest.
> >
> > Signed-off-by: Wei Hu <weh@microsoft.com>

Sasha -- this patch and one other from Wei Hu for the Hyper-V frame buffer
driver should be ready.  Both patches affect only the Hyper-V frame buffer
driver so can go through the Hyper-V tree.  Can you pick these up?  Thx.

Michael
Sasha Levin Oct. 1, 2019, 6:48 p.m. UTC | #4
On Fri, Sep 20, 2019 at 05:26:34PM +0000, Michael Kelley wrote:
>From: Michael Kelley <mikelley@microsoft.com>  Sent: Wednesday, September 18, 2019 2:48 PM
>> >
>> > Without deferred IO support, hyperv_fb driver informs the host to refresh
>> > the entire guest frame buffer at fixed rate, e.g. at 20Hz, no matter there
>> > is screen update or not. This patch supports deferred IO for screens in
>> > graphics mode and also enables the frame buffer on-demand refresh. The
>> > highest refresh rate is still set at 20Hz.
>> >
>> > Currently Hyper-V only takes a physical address from guest as the starting
>> > address of frame buffer. This implies the guest must allocate contiguous
>> > physical memory for frame buffer. In addition, Hyper-V Gen 2 VMs only
>> > accept address from MMIO region as frame buffer address. Due to these
>> > limitations on Hyper-V host, we keep a shadow copy of frame buffer
>> > in the guest. This means one more copy of the dirty rectangle inside
>> > guest when doing the on-demand refresh. This can be optimized in the
>> > future with help from host. For now the host performance gain from deferred
>> > IO outweighs the shadow copy impact in the guest.
>> >
>> > Signed-off-by: Wei Hu <weh@microsoft.com>
>
>Sasha -- this patch and one other from Wei Hu for the Hyper-V frame buffer
>driver should be ready.  Both patches affect only the Hyper-V frame buffer
>driver so can go through the Hyper-V tree.  Can you pick these up?  Thx.

I can't get this to apply anywhere, what tree is it based on?

--
Thanks,
Sasha
Dexuan Cui Oct. 2, 2019, 8:09 a.m. UTC | #5
> -----Original Message-----
> From: Sasha Levin <sashal@kernel.org>
> Sent: Tuesday, October 1, 2019 11:48 AM
> 
> On Fri, Sep 20, 2019 at 05:26:34PM +0000, Michael Kelley wrote:
> >From: Michael Kelley <mikelley@microsoft.com>  Sent: Wednesday,
> September 18, 2019 2:48 PM
> >> >
> >> > Without deferred IO support, hyperv_fb driver informs the host to refresh
> >> > the entire guest frame buffer at fixed rate, e.g. at 20Hz, no matter there
> >> > is screen update or not. This patch supports deferred IO for screens in
> >> > graphics mode and also enables the frame buffer on-demand refresh. The
> >> > highest refresh rate is still set at 20Hz.
> >> >
> >> > Currently Hyper-V only takes a physical address from guest as the starting
> >> > address of frame buffer. This implies the guest must allocate contiguous
> >> > physical memory for frame buffer. In addition, Hyper-V Gen 2 VMs only
> >> > accept address from MMIO region as frame buffer address. Due to these
> >> > limitations on Hyper-V host, we keep a shadow copy of frame buffer
> >> > in the guest. This means one more copy of the dirty rectangle inside
> >> > guest when doing the on-demand refresh. This can be optimized in the
> >> > future with help from host. For now the host performance gain from
> deferred
> >> > IO outweighs the shadow copy impact in the guest.
> >> >
> >> > Signed-off-by: Wei Hu <weh@microsoft.com>
> >
> >Sasha -- this patch and one other from Wei Hu for the Hyper-V frame buffer
> >driver should be ready.  Both patches affect only the Hyper-V frame buffer
> >driver so can go through the Hyper-V tree.  Can you pick these up?  Thx.
> 
> I can't get this to apply anywhere, what tree is it based on?
> 
> --
> Thanks,
> Sasha

Hi Sasha,
Today's hyperv/linux.git's hyperv-next branch's top commit is
    48b72a2697d5 ("hv_netvsc: Add the support of hibernation").

Please pick up two patches from Wei Hu:
#1: [PATCH v4] video: hyperv: hyperv_fb: Obtain screen resolution from Hyper-V host
#2: [PATHC v6] video: hyperv: hyperv_fb: Support deferred IO for Hyper-V frame buffer driver

I can apply the 2 patches on the hyperv-next branch (the top commit is 48b72a2697d5):

[decui@localhost linux]$ patch -p1 < 1.diff
patching file drivers/video/fbdev/hyperv_fb.c
Hunk #2 succeeded at 53 (offset 1 line).
Hunk #3 succeeded at 95 (offset 1 line).
Hunk #4 succeeded at 124 (offset 1 line).
Hunk #5 succeeded at 222 (offset 1 line).
Hunk #6 succeeded at 262 (offset 2 lines).
Hunk #7 succeeded at 394 (offset 2 lines).
Hunk #8 succeeded at 441 (offset 2 lines).
Hunk #9 succeeded at 480 (offset 2 lines).
Hunk #10 succeeded at 558 (offset 2 lines).
Hunk #11 succeeded at 590 (offset 2 lines).
Hunk #12 succeeded at 785 (offset 2 lines).
Hunk #13 succeeded at 823 (offset 2 lines).

[decui@localhost linux]$ patch -p1 < 2.diff
patching file drivers/video/fbdev/Kconfig
Hunk #1 succeeded at 2214 (offset -27 lines).
patching file drivers/video/fbdev/hyperv_fb.c
Hunk #1 succeeded at 238 (offset 1 line).
Hunk #2 succeeded at 259 (offset 2 lines).
Hunk #3 succeeded at 277 (offset 2 lines).
Hunk #4 succeeded at 364 (offset 2 lines).
Hunk #5 succeeded at 692 (offset 2 lines).
Hunk #6 succeeded at 702 (offset 2 lines).
Hunk #7 succeeded at 719 (offset 2 lines).
Hunk #8 succeeded at 801 (offset 2 lines).
Hunk #9 succeeded at 863 (offset 2 lines).
Hunk #10 succeeded at 876 (offset 2 lines).
Hunk #11 succeeded at 889 (offset 2 lines).
Hunk #12 succeeded at 951 (offset 2 lines).
Hunk #13 succeeded at 988 (offset 2 lines).
Hunk #14 succeeded at 1007 (offset 2 lines).
Hunk #15 succeeded at 1041 (offset 2 lines).

Thanks,
-- Dexuan
Sasha Levin Oct. 2, 2019, 12:40 p.m. UTC | #6
On Wed, Oct 02, 2019 at 08:09:41AM +0000, Dexuan Cui wrote:
>> -----Original Message-----
>> From: Sasha Levin <sashal@kernel.org>
>> Sent: Tuesday, October 1, 2019 11:48 AM
>>
>> On Fri, Sep 20, 2019 at 05:26:34PM +0000, Michael Kelley wrote:
>> >From: Michael Kelley <mikelley@microsoft.com>  Sent: Wednesday,
>> September 18, 2019 2:48 PM
>> >> >
>> >> > Without deferred IO support, hyperv_fb driver informs the host to refresh
>> >> > the entire guest frame buffer at fixed rate, e.g. at 20Hz, no matter there
>> >> > is screen update or not. This patch supports deferred IO for screens in
>> >> > graphics mode and also enables the frame buffer on-demand refresh. The
>> >> > highest refresh rate is still set at 20Hz.
>> >> >
>> >> > Currently Hyper-V only takes a physical address from guest as the starting
>> >> > address of frame buffer. This implies the guest must allocate contiguous
>> >> > physical memory for frame buffer. In addition, Hyper-V Gen 2 VMs only
>> >> > accept address from MMIO region as frame buffer address. Due to these
>> >> > limitations on Hyper-V host, we keep a shadow copy of frame buffer
>> >> > in the guest. This means one more copy of the dirty rectangle inside
>> >> > guest when doing the on-demand refresh. This can be optimized in the
>> >> > future with help from host. For now the host performance gain from
>> deferred
>> >> > IO outweighs the shadow copy impact in the guest.
>> >> >
>> >> > Signed-off-by: Wei Hu <weh@microsoft.com>
>> >
>> >Sasha -- this patch and one other from Wei Hu for the Hyper-V frame buffer
>> >driver should be ready.  Both patches affect only the Hyper-V frame buffer
>> >driver so can go through the Hyper-V tree.  Can you pick these up?  Thx.
>>
>> I can't get this to apply anywhere, what tree is it based on?
>>
>> --
>> Thanks,
>> Sasha
>
>Hi Sasha,
>Today's hyperv/linux.git's hyperv-next branch's top commit is
>    48b72a2697d5 ("hv_netvsc: Add the support of hibernation").
>
>Please pick up two patches from Wei Hu:
>#1: [PATCH v4] video: hyperv: hyperv_fb: Obtain screen resolution from Hyper-V host
>#2: [PATHC v6] video: hyperv: hyperv_fb: Support deferred IO for Hyper-V frame buffer driver

Ah, I guess I was missing the first one. I've queued both for
hyperv-next, thanks!

--
Thanks,
Sasha
diff mbox series

Patch

diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index 1b2f5f31fb6f..e781f89a1824 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -2241,6 +2241,7 @@  config FB_HYPERV
 	select FB_CFB_FILLRECT
 	select FB_CFB_COPYAREA
 	select FB_CFB_IMAGEBLIT
+	select FB_DEFERRED_IO
 	help
 	  This framebuffer driver supports Microsoft Hyper-V Synthetic Video.
 
diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c
index fe319fc39bec..0c57445f8357 100644
--- a/drivers/video/fbdev/hyperv_fb.c
+++ b/drivers/video/fbdev/hyperv_fb.c
@@ -237,6 +237,7 @@  struct synthvid_msg {
 #define RING_BUFSIZE (256 * 1024)
 #define VSP_TIMEOUT (10 * HZ)
 #define HVFB_UPDATE_DELAY (HZ / 20)
+#define HVFB_ONDEMAND_THROTTLE (HZ / 20)
 
 struct hvfb_par {
 	struct fb_info *info;
@@ -256,6 +257,16 @@  struct hvfb_par {
 	bool synchronous_fb;
 
 	struct notifier_block hvfb_panic_nb;
+
+	/* Memory for deferred IO and frame buffer itself */
+	unsigned char *dio_vp;
+	unsigned char *mmio_vp;
+	unsigned long mmio_pp;
+
+	/* Dirty rectangle, protected by delayed_refresh_lock */
+	int x1, y1, x2, y2;
+	bool delayed_refresh;
+	spinlock_t delayed_refresh_lock;
 };
 
 static uint screen_width = HVFB_WIDTH;
@@ -264,6 +275,7 @@  static uint screen_width_max = HVFB_WIDTH;
 static uint screen_height_max = HVFB_HEIGHT;
 static uint screen_depth;
 static uint screen_fb_size;
+static uint dio_fb_size; /* FB size for deferred IO */
 
 /* Send message to Hyper-V host */
 static inline int synthvid_send(struct hv_device *hdev,
@@ -350,28 +362,88 @@  static int synthvid_send_ptr(struct hv_device *hdev)
 }
 
 /* Send updated screen area (dirty rectangle) location to host */
-static int synthvid_update(struct fb_info *info)
+static int
+synthvid_update(struct fb_info *info, int x1, int y1, int x2, int y2)
 {
 	struct hv_device *hdev = device_to_hv_device(info->device);
 	struct synthvid_msg msg;
 
 	memset(&msg, 0, sizeof(struct synthvid_msg));
+	if (x2 == INT_MAX)
+		x2 = info->var.xres;
+	if (y2 == INT_MAX)
+		y2 = info->var.yres;
 
 	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;
+	msg.dirt.rect[0].x1 = (x1 > x2) ? 0 : x1;
+	msg.dirt.rect[0].y1 = (y1 > y2) ? 0 : y1;
+	msg.dirt.rect[0].x2 =
+		(x2 < x1 || x2 > info->var.xres) ? info->var.xres : x2;
+	msg.dirt.rect[0].y2 =
+		(y2 < y1 || y2 > info->var.yres) ? info->var.yres : y2;
 
 	synthvid_send(hdev, &msg);
 
 	return 0;
 }
 
+static void hvfb_docopy(struct hvfb_par *par,
+			unsigned long offset,
+			unsigned long size)
+{
+	if (!par || !par->mmio_vp || !par->dio_vp || !par->fb_ready ||
+	    size == 0 || offset >= dio_fb_size)
+		return;
+
+	if (offset + size > dio_fb_size)
+		size = dio_fb_size - offset;
+
+	memcpy(par->mmio_vp + offset, par->dio_vp + offset, size);
+}
+
+/* Deferred IO callback */
+static void synthvid_deferred_io(struct fb_info *p,
+				 struct list_head *pagelist)
+{
+	struct hvfb_par *par = p->par;
+	struct page *page;
+	unsigned long start, end;
+	int y1, y2, miny, maxy;
+
+	miny = INT_MAX;
+	maxy = 0;
+
+	/*
+	 * Merge dirty pages. It is possible that last page cross
+	 * over the end of frame buffer row yres. This is taken care of
+	 * in synthvid_update function by clamping the y2
+	 * value to yres.
+	 */
+	list_for_each_entry(page, pagelist, lru) {
+		start = page->index << PAGE_SHIFT;
+		end = start + PAGE_SIZE - 1;
+		y1 = start / p->fix.line_length;
+		y2 = end / p->fix.line_length;
+		miny = min_t(int, miny, y1);
+		maxy = max_t(int, maxy, y2);
+
+		/* Copy from dio space to mmio address */
+		if (par->fb_ready)
+			hvfb_docopy(par, start, PAGE_SIZE);
+	}
+
+	if (par->fb_ready && par->update)
+		synthvid_update(p, 0, miny, p->var.xres, maxy + 1);
+}
+
+static struct fb_deferred_io synthvid_defio = {
+	.delay		= HZ / 20,
+	.deferred_io	= synthvid_deferred_io,
+};
 
 /*
  * Actions on received messages from host:
@@ -618,7 +690,7 @@  static int synthvid_send_config(struct hv_device *hdev)
 	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.user_ctx = msg->vram.vram_gpa = par->mmio_pp;
 	msg->vram.is_vram_gpa_specified = 1;
 	synthvid_send(hdev, msg);
 
@@ -628,7 +700,7 @@  static int synthvid_send_config(struct hv_device *hdev)
 		ret = -ETIMEDOUT;
 		goto out;
 	}
-	if (msg->vram_ack.user_ctx != info->fix.smem_start) {
+	if (msg->vram_ack.user_ctx != par->mmio_pp) {
 		pr_err("Unable to set VRAM location\n");
 		ret = -ENODEV;
 		goto out;
@@ -645,19 +717,77 @@  static int synthvid_send_config(struct hv_device *hdev)
 
 /*
  * 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.
+ * It is scheduled to call whenever update request is received and it has
+ * not been called in last HVFB_ONDEMAND_THROTTLE time interval.
  */
 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;
+	unsigned long flags;
+	int x1, x2, y1, y2;
+	int j;
+
+	spin_lock_irqsave(&par->delayed_refresh_lock, flags);
+	/* Reset the request flag */
+	par->delayed_refresh = false;
+
+	/* Store the dirty rectangle to local variables */
+	x1 = par->x1;
+	x2 = par->x2;
+	y1 = par->y1;
+	y2 = par->y2;
+
+	/* Clear dirty rectangle */
+	par->x1 = par->y1 = INT_MAX;
+	par->x2 = par->y2 = 0;
+
+	spin_unlock_irqrestore(&par->delayed_refresh_lock, flags);
+
+	if (x1 > info->var.xres || x2 > info->var.xres ||
+	    y1 > info->var.yres || y2 > info->var.yres || x2 <= x1)
+		return;
+
+	/* Copy the dirty rectangle to frame buffer memory */
+	for (j = y1; j < y2; j++) {
+		hvfb_docopy(par,
+			    j * info->fix.line_length +
+			    (x1 * screen_depth / 8),
+			    (x2 - x1) * screen_depth / 8);
+	}
+
+	/* Refresh */
+	if (par->fb_ready && par->update)
+		synthvid_update(info, x1, y1, x2, y2);
+}
 
-	if (par->fb_ready)
-		synthvid_update(info);
+/*
+ * Control the on-demand refresh frequency. It schedules a delayed
+ * screen update if it has not yet.
+ */
+static void hvfb_ondemand_refresh_throttle(struct hvfb_par *par,
+					   int x1, int y1, int w, int h)
+{
+	unsigned long flags;
+	int x2 = x1 + w;
+	int y2 = y1 + h;
+
+	spin_lock_irqsave(&par->delayed_refresh_lock, flags);
+
+	/* Merge dirty rectangle */
+	par->x1 = min_t(int, par->x1, x1);
+	par->y1 = min_t(int, par->y1, y1);
+	par->x2 = max_t(int, par->x2, x2);
+	par->y2 = max_t(int, par->y2, y2);
+
+	/* Schedule a delayed screen update if not yet */
+	if (par->delayed_refresh == false) {
+		schedule_delayed_work(&par->dwork,
+				      HVFB_ONDEMAND_THROTTLE);
+		par->delayed_refresh = true;
+	}
 
-	if (par->update)
-		schedule_delayed_work(&par->dwork, HVFB_UPDATE_DELAY);
+	spin_unlock_irqrestore(&par->delayed_refresh_lock, flags);
 }
 
 static int hvfb_on_panic(struct notifier_block *nb,
@@ -669,7 +799,8 @@  static int hvfb_on_panic(struct notifier_block *nb,
 	par = container_of(nb, struct hvfb_par, hvfb_panic_nb);
 	par->synchronous_fb = true;
 	info = par->info;
-	synthvid_update(info);
+	hvfb_docopy(par, 0, dio_fb_size);
+	synthvid_update(info, 0, 0, INT_MAX, INT_MAX);
 
 	return NOTIFY_DONE;
 }
@@ -730,7 +861,10 @@  static void hvfb_cfb_fillrect(struct fb_info *p,
 
 	cfb_fillrect(p, rect);
 	if (par->synchronous_fb)
-		synthvid_update(p);
+		synthvid_update(p, 0, 0, INT_MAX, INT_MAX);
+	else
+		hvfb_ondemand_refresh_throttle(par, rect->dx, rect->dy,
+					       rect->width, rect->height);
 }
 
 static void hvfb_cfb_copyarea(struct fb_info *p,
@@ -740,7 +874,10 @@  static void hvfb_cfb_copyarea(struct fb_info *p,
 
 	cfb_copyarea(p, area);
 	if (par->synchronous_fb)
-		synthvid_update(p);
+		synthvid_update(p, 0, 0, INT_MAX, INT_MAX);
+	else
+		hvfb_ondemand_refresh_throttle(par, area->dx, area->dy,
+					       area->width, area->height);
 }
 
 static void hvfb_cfb_imageblit(struct fb_info *p,
@@ -750,7 +887,10 @@  static void hvfb_cfb_imageblit(struct fb_info *p,
 
 	cfb_imageblit(p, image);
 	if (par->synchronous_fb)
-		synthvid_update(p);
+		synthvid_update(p, 0, 0, INT_MAX, INT_MAX);
+	else
+		hvfb_ondemand_refresh_throttle(par, image->dx, image->dy,
+					       image->width, image->height);
 }
 
 static struct fb_ops hvfb_ops = {
@@ -809,6 +949,9 @@  static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info)
 	resource_size_t pot_start, pot_end;
 	int ret;
 
+	dio_fb_size =
+		screen_width * screen_height * screen_depth / 8;
+
 	if (gen2vm) {
 		pot_start = 0;
 		pot_end = -1;
@@ -843,9 +986,14 @@  static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info)
 	if (!fb_virt)
 		goto err2;
 
+	/* Allocate memory for deferred IO */
+	par->dio_vp = vzalloc(round_up(dio_fb_size, PAGE_SIZE));
+	if (par->dio_vp == NULL)
+		goto err3;
+
 	info->apertures = alloc_apertures(1);
 	if (!info->apertures)
-		goto err3;
+		goto err4;
 
 	if (gen2vm) {
 		info->apertures->ranges[0].base = screen_info.lfb_base;
@@ -857,16 +1005,23 @@  static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info)
 		info->apertures->ranges[0].size = pci_resource_len(pdev, 0);
 	}
 
+	/* Physical address of FB device */
+	par->mmio_pp = par->mem->start;
+	/* Virtual address of FB device */
+	par->mmio_vp = (unsigned char *) fb_virt;
+
 	info->fix.smem_start = par->mem->start;
-	info->fix.smem_len = screen_fb_size;
-	info->screen_base = fb_virt;
-	info->screen_size = screen_fb_size;
+	info->fix.smem_len = dio_fb_size;
+	info->screen_base = par->dio_vp;
+	info->screen_size = dio_fb_size;
 
 	if (!gen2vm)
 		pci_dev_put(pdev);
 
 	return 0;
 
+err4:
+	vfree(par->dio_vp);
 err3:
 	iounmap(fb_virt);
 err2:
@@ -884,6 +1039,7 @@  static void hvfb_putmem(struct fb_info *info)
 {
 	struct hvfb_par *par = info->par;
 
+	vfree(par->dio_vp);
 	iounmap(info->screen_base);
 	vmbus_free_mmio(par->mem->start, screen_fb_size);
 	par->mem = NULL;
@@ -909,6 +1065,11 @@  static int hvfb_probe(struct hv_device *hdev,
 	init_completion(&par->wait);
 	INIT_DELAYED_WORK(&par->dwork, hvfb_update_work);
 
+	par->delayed_refresh = false;
+	spin_lock_init(&par->delayed_refresh_lock);
+	par->x1 = par->y1 = INT_MAX;
+	par->x2 = par->y2 = 0;
+
 	/* Connect to VSP */
 	hv_set_drvdata(hdev, info);
 	ret = synthvid_connect_vsp(hdev);
@@ -960,6 +1121,10 @@  static int hvfb_probe(struct hv_device *hdev,
 	info->fbops = &hvfb_ops;
 	info->pseudo_palette = par->pseudo_palette;
 
+	/* Initialize deferred IO */
+	info->fbdefio = &synthvid_defio;
+	fb_deferred_io_init(info);
+
 	/* Send config to host */
 	ret = synthvid_send_config(hdev);
 	if (ret)
@@ -981,6 +1146,7 @@  static int hvfb_probe(struct hv_device *hdev,
 	return 0;
 
 error:
+	fb_deferred_io_cleanup(info);
 	hvfb_putmem(info);
 error2:
 	vmbus_close(hdev->channel);
@@ -1003,6 +1169,8 @@  static int hvfb_remove(struct hv_device *hdev)
 	par->update = false;
 	par->fb_ready = false;
 
+	fb_deferred_io_cleanup(info);
+
 	unregister_framebuffer(info);
 	cancel_delayed_work_sync(&par->dwork);