Olaf Hering cc714a
From: Wei Hu <weh@microsoft.com>
Olaf Hering cc714a
Date: Wed, 18 Sep 2019 06:03:20 +0000
Olaf Hering cc714a
Patch-mainline: v5.5-rc1
Olaf Hering cc714a
References: bsc#1175306
Olaf Hering cc714a
Subject: video: hyperv: hyperv_fb: Support deferred IO for Hyper-V frame buffer driver
Olaf Hering cc714a
Git-commit: d21987d709e807ba7bbf47044deb56a3c02e8be4
Olaf Hering cc714a
Olaf Hering cc714a
Without deferred IO support, hyperv_fb driver informs the host to refresh
Olaf Hering cc714a
the entire guest frame buffer at fixed rate, e.g. at 20Hz, no matter there
Olaf Hering cc714a
is screen update or not. This patch supports deferred IO for screens in
Olaf Hering cc714a
graphics mode and also enables the frame buffer on-demand refresh. The
Olaf Hering cc714a
highest refresh rate is still set at 20Hz.
Olaf Hering cc714a
Olaf Hering cc714a
Currently Hyper-V only takes a physical address from guest as the starting
Olaf Hering cc714a
address of frame buffer. This implies the guest must allocate contiguous
Olaf Hering cc714a
physical memory for frame buffer. In addition, Hyper-V Gen 2 VMs only
Olaf Hering cc714a
accept address from MMIO region as frame buffer address. Due to these
Olaf Hering cc714a
limitations on Hyper-V host, we keep a shadow copy of frame buffer
Olaf Hering cc714a
in the guest. This means one more copy of the dirty rectangle inside
Olaf Hering cc714a
guest when doing the on-demand refresh. This can be optimized in the
Olaf Hering cc714a
future with help from host. For now the host performance gain from deferred
Olaf Hering cc714a
IO outweighs the shadow copy impact in the guest.
Olaf Hering cc714a
Olaf Hering cc714a
Signed-off-by: Wei Hu <weh@microsoft.com>
Olaf Hering cc714a
Reviewed-by: Dexuan Cui <decui@microsoft.com>
Olaf Hering cc714a
Reviewed-by: Michael Kelley <mikelley@microsoft.com>
Olaf Hering cc714a
Signed-off-by: Sasha Levin <sashal@kernel.org>
Olaf Hering cc714a
Acked-by: Olaf Hering <ohering@suse.de>
Olaf Hering cc714a
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Olaf Hering cc714a
---
Olaf Hering cc714a
 drivers/video/fbdev/Kconfig     |   1 +
Olaf Hering cc714a
 drivers/video/fbdev/hyperv_fb.c | 210 ++++++++++++++++++++++++++++++++++++----
Olaf Hering cc714a
 2 files changed, 190 insertions(+), 21 deletions(-)
Olaf Hering cc714a
Olaf Hering cc714a
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
Olaf Hering cc714a
--- a/drivers/video/fbdev/Kconfig
Olaf Hering cc714a
+++ b/drivers/video/fbdev/Kconfig
Olaf Hering cc714a
@@ -2214,6 +2214,7 @@ config FB_HYPERV
Olaf Hering cc714a
 	select FB_CFB_FILLRECT
Olaf Hering cc714a
 	select FB_CFB_COPYAREA
Olaf Hering cc714a
 	select FB_CFB_IMAGEBLIT
Olaf Hering cc714a
+	select FB_DEFERRED_IO
Olaf Hering cc714a
 	help
Olaf Hering cc714a
 	  This framebuffer driver supports Microsoft Hyper-V Synthetic Video.
Olaf Hering cc714a
 
Olaf Hering cc714a
diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c
Olaf Hering cc714a
--- a/drivers/video/fbdev/hyperv_fb.c
Olaf Hering cc714a
+++ b/drivers/video/fbdev/hyperv_fb.c
Olaf Hering cc714a
@@ -238,6 +238,7 @@ struct synthvid_msg {
Olaf Hering cc714a
 #define RING_BUFSIZE (256 * 1024)
Olaf Hering cc714a
 #define VSP_TIMEOUT (10 * HZ)
Olaf Hering cc714a
 #define HVFB_UPDATE_DELAY (HZ / 20)
Olaf Hering cc714a
+#define HVFB_ONDEMAND_THROTTLE (HZ / 20)
Olaf Hering cc714a
 
Olaf Hering cc714a
 struct hvfb_par {
Olaf Hering cc714a
 	struct fb_info *info;
Olaf Hering cc714a
@@ -258,6 +259,16 @@ struct hvfb_par {
Olaf Hering cc714a
 	bool synchronous_fb;
Olaf Hering cc714a
 
Olaf Hering cc714a
 	struct notifier_block hvfb_panic_nb;
Olaf Hering cc714a
+
Olaf Hering cc714a
+	/* Memory for deferred IO and frame buffer itself */
Olaf Hering cc714a
+	unsigned char *dio_vp;
Olaf Hering cc714a
+	unsigned char *mmio_vp;
Olaf Hering cc714a
+	unsigned long mmio_pp;
Olaf Hering cc714a
+
Olaf Hering cc714a
+	/* Dirty rectangle, protected by delayed_refresh_lock */
Olaf Hering cc714a
+	int x1, y1, x2, y2;
Olaf Hering cc714a
+	bool delayed_refresh;
Olaf Hering cc714a
+	spinlock_t delayed_refresh_lock;
Olaf Hering cc714a
 };
Olaf Hering cc714a
 
Olaf Hering cc714a
 static uint screen_width = HVFB_WIDTH;
Olaf Hering cc714a
@@ -266,6 +277,7 @@ static uint screen_width_max = HVFB_WIDTH;
Olaf Hering cc714a
 static uint screen_height_max = HVFB_HEIGHT;
Olaf Hering cc714a
 static uint screen_depth;
Olaf Hering cc714a
 static uint screen_fb_size;
Olaf Hering cc714a
+static uint dio_fb_size; /* FB size for deferred IO */
Olaf Hering cc714a
 
Olaf Hering cc714a
 /* Send message to Hyper-V host */
Olaf Hering cc714a
 static inline int synthvid_send(struct hv_device *hdev,
Olaf Hering cc714a
@@ -352,28 +364,88 @@ static int synthvid_send_ptr(struct hv_device *hdev)
Olaf Hering cc714a
 }
Olaf Hering cc714a
 
Olaf Hering cc714a
 /* Send updated screen area (dirty rectangle) location to host */
Olaf Hering cc714a
-static int synthvid_update(struct fb_info *info)
Olaf Hering cc714a
+static int
Olaf Hering cc714a
+synthvid_update(struct fb_info *info, int x1, int y1, int x2, int y2)
Olaf Hering cc714a
 {
Olaf Hering cc714a
 	struct hv_device *hdev = device_to_hv_device(info->device);
Olaf Hering cc714a
 	struct synthvid_msg msg;
Olaf Hering cc714a
 
Olaf Hering cc714a
 	memset(&msg, 0, sizeof(struct synthvid_msg));
Olaf Hering cc714a
+	if (x2 == INT_MAX)
Olaf Hering cc714a
+		x2 = info->var.xres;
Olaf Hering cc714a
+	if (y2 == INT_MAX)
Olaf Hering cc714a
+		y2 = info->var.yres;
Olaf Hering cc714a
 
Olaf Hering cc714a
 	msg.vid_hdr.type = SYNTHVID_DIRT;
Olaf Hering cc714a
 	msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
Olaf Hering cc714a
 		sizeof(struct synthvid_dirt);
Olaf Hering cc714a
 	msg.dirt.video_output = 0;
Olaf Hering cc714a
 	msg.dirt.dirt_count = 1;
Olaf Hering cc714a
-	msg.dirt.rect[0].x1 = 0;
Olaf Hering cc714a
-	msg.dirt.rect[0].y1 = 0;
Olaf Hering cc714a
-	msg.dirt.rect[0].x2 = info->var.xres;
Olaf Hering cc714a
-	msg.dirt.rect[0].y2 = info->var.yres;
Olaf Hering cc714a
+	msg.dirt.rect[0].x1 = (x1 > x2) ? 0 : x1;
Olaf Hering cc714a
+	msg.dirt.rect[0].y1 = (y1 > y2) ? 0 : y1;
Olaf Hering cc714a
+	msg.dirt.rect[0].x2 =
Olaf Hering cc714a
+		(x2 < x1 || x2 > info->var.xres) ? info->var.xres : x2;
Olaf Hering cc714a
+	msg.dirt.rect[0].y2 =
Olaf Hering cc714a
+		(y2 < y1 || y2 > info->var.yres) ? info->var.yres : y2;
Olaf Hering cc714a
 
Olaf Hering cc714a
 	synthvid_send(hdev, &msg;;
Olaf Hering cc714a
 
Olaf Hering cc714a
 	return 0;
Olaf Hering cc714a
 }
Olaf Hering cc714a
 
Olaf Hering cc714a
+static void hvfb_docopy(struct hvfb_par *par,
Olaf Hering cc714a
+			unsigned long offset,
Olaf Hering cc714a
+			unsigned long size)
Olaf Hering cc714a
+{
Olaf Hering cc714a
+	if (!par || !par->mmio_vp || !par->dio_vp || !par->fb_ready ||
Olaf Hering cc714a
+	    size == 0 || offset >= dio_fb_size)
Olaf Hering cc714a
+		return;
Olaf Hering cc714a
+
Olaf Hering cc714a
+	if (offset + size > dio_fb_size)
Olaf Hering cc714a
+		size = dio_fb_size - offset;
Olaf Hering cc714a
+
Olaf Hering cc714a
+	memcpy(par->mmio_vp + offset, par->dio_vp + offset, size);
Olaf Hering cc714a
+}
Olaf Hering cc714a
+
Olaf Hering cc714a
+/* Deferred IO callback */
Olaf Hering cc714a
+static void synthvid_deferred_io(struct fb_info *p,
Olaf Hering cc714a
+				 struct list_head *pagelist)
Olaf Hering cc714a
+{
Olaf Hering cc714a
+	struct hvfb_par *par = p->par;
Olaf Hering cc714a
+	struct page *page;
Olaf Hering cc714a
+	unsigned long start, end;
Olaf Hering cc714a
+	int y1, y2, miny, maxy;
Olaf Hering cc714a
+
Olaf Hering cc714a
+	miny = INT_MAX;
Olaf Hering cc714a
+	maxy = 0;
Olaf Hering cc714a
+
Olaf Hering cc714a
+	/*
Olaf Hering cc714a
+	 * Merge dirty pages. It is possible that last page cross
Olaf Hering cc714a
+	 * over the end of frame buffer row yres. This is taken care of
Olaf Hering cc714a
+	 * in synthvid_update function by clamping the y2
Olaf Hering cc714a
+	 * value to yres.
Olaf Hering cc714a
+	 */
Olaf Hering cc714a
+	list_for_each_entry(page, pagelist, lru) {
Olaf Hering cc714a
+		start = page->index << PAGE_SHIFT;
Olaf Hering cc714a
+		end = start + PAGE_SIZE - 1;
Olaf Hering cc714a
+		y1 = start / p->fix.line_length;
Olaf Hering cc714a
+		y2 = end / p->fix.line_length;
Olaf Hering cc714a
+		miny = min_t(int, miny, y1);
Olaf Hering cc714a
+		maxy = max_t(int, maxy, y2);
Olaf Hering cc714a
+
Olaf Hering cc714a
+		/* Copy from dio space to mmio address */
Olaf Hering cc714a
+		if (par->fb_ready)
Olaf Hering cc714a
+			hvfb_docopy(par, start, PAGE_SIZE);
Olaf Hering cc714a
+	}
Olaf Hering cc714a
+
Olaf Hering cc714a
+	if (par->fb_ready && par->update)
Olaf Hering cc714a
+		synthvid_update(p, 0, miny, p->var.xres, maxy + 1);
Olaf Hering cc714a
+}
Olaf Hering cc714a
+
Olaf Hering cc714a
+static struct fb_deferred_io synthvid_defio = {
Olaf Hering cc714a
+	.delay		= HZ / 20,
Olaf Hering cc714a
+	.deferred_io	= synthvid_deferred_io,
Olaf Hering cc714a
+};
Olaf Hering cc714a
 
Olaf Hering cc714a
 /*
Olaf Hering cc714a
  * Actions on received messages from host:
Olaf Hering cc714a
@@ -620,7 +692,7 @@ static int synthvid_send_config(struct hv_device *hdev)
Olaf Hering cc714a
 	msg->vid_hdr.type = SYNTHVID_VRAM_LOCATION;
Olaf Hering cc714a
 	msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
Olaf Hering cc714a
 		sizeof(struct synthvid_vram_location);
Olaf Hering cc714a
-	msg->vram.user_ctx = msg->vram.vram_gpa = info->fix.smem_start;
Olaf Hering cc714a
+	msg->vram.user_ctx = msg->vram.vram_gpa = par->mmio_pp;
Olaf Hering cc714a
 	msg->vram.is_vram_gpa_specified = 1;
Olaf Hering cc714a
 	synthvid_send(hdev, msg);
Olaf Hering cc714a
 
Olaf Hering cc714a
@@ -630,7 +702,7 @@ static int synthvid_send_config(struct hv_device *hdev)
Olaf Hering cc714a
 		ret = -ETIMEDOUT;
Olaf Hering cc714a
 		goto out;
Olaf Hering cc714a
 	}
Olaf Hering cc714a
-	if (msg->vram_ack.user_ctx != info->fix.smem_start) {
Olaf Hering cc714a
+	if (msg->vram_ack.user_ctx != par->mmio_pp) {
Olaf Hering cc714a
 		pr_err("Unable to set VRAM location\n");
Olaf Hering cc714a
 		ret = -ENODEV;
Olaf Hering cc714a
 		goto out;
Olaf Hering cc714a
@@ -647,19 +719,77 @@ out:
Olaf Hering cc714a
 
Olaf Hering cc714a
 /*
Olaf Hering cc714a
  * Delayed work callback:
Olaf Hering cc714a
- * It is called at HVFB_UPDATE_DELAY or longer time interval to process
Olaf Hering cc714a
- * screen updates. It is re-scheduled if further update is necessary.
Olaf Hering cc714a
+ * It is scheduled to call whenever update request is received and it has
Olaf Hering cc714a
+ * not been called in last HVFB_ONDEMAND_THROTTLE time interval.
Olaf Hering cc714a
  */
Olaf Hering cc714a
 static void hvfb_update_work(struct work_struct *w)
Olaf Hering cc714a
 {
Olaf Hering cc714a
 	struct hvfb_par *par = container_of(w, struct hvfb_par, dwork.work);
Olaf Hering cc714a
 	struct fb_info *info = par->info;
Olaf Hering cc714a
+	unsigned long flags;
Olaf Hering cc714a
+	int x1, x2, y1, y2;
Olaf Hering cc714a
+	int j;
Olaf Hering cc714a
+
Olaf Hering cc714a
+	spin_lock_irqsave(&par->delayed_refresh_lock, flags);
Olaf Hering cc714a
+	/* Reset the request flag */
Olaf Hering cc714a
+	par->delayed_refresh = false;
Olaf Hering cc714a
+
Olaf Hering cc714a
+	/* Store the dirty rectangle to local variables */
Olaf Hering cc714a
+	x1 = par->x1;
Olaf Hering cc714a
+	x2 = par->x2;
Olaf Hering cc714a
+	y1 = par->y1;
Olaf Hering cc714a
+	y2 = par->y2;
Olaf Hering cc714a
+
Olaf Hering cc714a
+	/* Clear dirty rectangle */
Olaf Hering cc714a
+	par->x1 = par->y1 = INT_MAX;
Olaf Hering cc714a
+	par->x2 = par->y2 = 0;
Olaf Hering cc714a
+
Olaf Hering cc714a
+	spin_unlock_irqrestore(&par->delayed_refresh_lock, flags);
Olaf Hering cc714a
+
Olaf Hering cc714a
+	if (x1 > info->var.xres || x2 > info->var.xres ||
Olaf Hering cc714a
+	    y1 > info->var.yres || y2 > info->var.yres || x2 <= x1)
Olaf Hering cc714a
+		return;
Olaf Hering cc714a
+
Olaf Hering cc714a
+	/* Copy the dirty rectangle to frame buffer memory */
Olaf Hering cc714a
+	for (j = y1; j < y2; j++) {
Olaf Hering cc714a
+		hvfb_docopy(par,
Olaf Hering cc714a
+			    j * info->fix.line_length +
Olaf Hering cc714a
+			    (x1 * screen_depth / 8),
Olaf Hering cc714a
+			    (x2 - x1) * screen_depth / 8);
Olaf Hering cc714a
+	}
Olaf Hering cc714a
+
Olaf Hering cc714a
+	/* Refresh */
Olaf Hering cc714a
+	if (par->fb_ready && par->update)
Olaf Hering cc714a
+		synthvid_update(info, x1, y1, x2, y2);
Olaf Hering cc714a
+}
Olaf Hering cc714a
 
Olaf Hering cc714a
-	if (par->fb_ready)
Olaf Hering cc714a
-		synthvid_update(info);
Olaf Hering cc714a
+/*
Olaf Hering cc714a
+ * Control the on-demand refresh frequency. It schedules a delayed
Olaf Hering cc714a
+ * screen update if it has not yet.
Olaf Hering cc714a
+ */
Olaf Hering cc714a
+static void hvfb_ondemand_refresh_throttle(struct hvfb_par *par,
Olaf Hering cc714a
+					   int x1, int y1, int w, int h)
Olaf Hering cc714a
+{
Olaf Hering cc714a
+	unsigned long flags;
Olaf Hering cc714a
+	int x2 = x1 + w;
Olaf Hering cc714a
+	int y2 = y1 + h;
Olaf Hering cc714a
+
Olaf Hering cc714a
+	spin_lock_irqsave(&par->delayed_refresh_lock, flags);
Olaf Hering cc714a
+
Olaf Hering cc714a
+	/* Merge dirty rectangle */
Olaf Hering cc714a
+	par->x1 = min_t(int, par->x1, x1);
Olaf Hering cc714a
+	par->y1 = min_t(int, par->y1, y1);
Olaf Hering cc714a
+	par->x2 = max_t(int, par->x2, x2);
Olaf Hering cc714a
+	par->y2 = max_t(int, par->y2, y2);
Olaf Hering cc714a
+
Olaf Hering cc714a
+	/* Schedule a delayed screen update if not yet */
Olaf Hering cc714a
+	if (par->delayed_refresh == false) {
Olaf Hering cc714a
+		schedule_delayed_work(&par->dwork,
Olaf Hering cc714a
+				      HVFB_ONDEMAND_THROTTLE);
Olaf Hering cc714a
+		par->delayed_refresh = true;
Olaf Hering cc714a
+	}
Olaf Hering cc714a
 
Olaf Hering cc714a
-	if (par->update)
Olaf Hering cc714a
-		schedule_delayed_work(&par->dwork, HVFB_UPDATE_DELAY);
Olaf Hering cc714a
+	spin_unlock_irqrestore(&par->delayed_refresh_lock, flags);
Olaf Hering cc714a
 }
Olaf Hering cc714a
 
Olaf Hering cc714a
 static int hvfb_on_panic(struct notifier_block *nb,
Olaf Hering cc714a
@@ -671,7 +801,8 @@ static int hvfb_on_panic(struct notifier_block *nb,
Olaf Hering cc714a
 	par = container_of(nb, struct hvfb_par, hvfb_panic_nb);
Olaf Hering cc714a
 	par->synchronous_fb = true;
Olaf Hering cc714a
 	info = par->info;
Olaf Hering cc714a
-	synthvid_update(info);
Olaf Hering cc714a
+	hvfb_docopy(par, 0, dio_fb_size);
Olaf Hering cc714a
+	synthvid_update(info, 0, 0, INT_MAX, INT_MAX);
Olaf Hering cc714a
 
Olaf Hering cc714a
 	return NOTIFY_DONE;
Olaf Hering cc714a
 }
Olaf Hering cc714a
@@ -732,7 +863,10 @@ static void hvfb_cfb_fillrect(struct fb_info *p,
Olaf Hering cc714a
 
Olaf Hering cc714a
 	cfb_fillrect(p, rect);
Olaf Hering cc714a
 	if (par->synchronous_fb)
Olaf Hering cc714a
-		synthvid_update(p);
Olaf Hering cc714a
+		synthvid_update(p, 0, 0, INT_MAX, INT_MAX);
Olaf Hering cc714a
+	else
Olaf Hering cc714a
+		hvfb_ondemand_refresh_throttle(par, rect->dx, rect->dy,
Olaf Hering cc714a
+					       rect->width, rect->height);
Olaf Hering cc714a
 }
Olaf Hering cc714a
 
Olaf Hering cc714a
 static void hvfb_cfb_copyarea(struct fb_info *p,
Olaf Hering cc714a
@@ -742,7 +876,10 @@ static void hvfb_cfb_copyarea(struct fb_info *p,
Olaf Hering cc714a
 
Olaf Hering cc714a
 	cfb_copyarea(p, area);
Olaf Hering cc714a
 	if (par->synchronous_fb)
Olaf Hering cc714a
-		synthvid_update(p);
Olaf Hering cc714a
+		synthvid_update(p, 0, 0, INT_MAX, INT_MAX);
Olaf Hering cc714a
+	else
Olaf Hering cc714a
+		hvfb_ondemand_refresh_throttle(par, area->dx, area->dy,
Olaf Hering cc714a
+					       area->width, area->height);
Olaf Hering cc714a
 }
Olaf Hering cc714a
 
Olaf Hering cc714a
 static void hvfb_cfb_imageblit(struct fb_info *p,
Olaf Hering cc714a
@@ -752,7 +889,10 @@ static void hvfb_cfb_imageblit(struct fb_info *p,
Olaf Hering cc714a
 
Olaf Hering cc714a
 	cfb_imageblit(p, image);
Olaf Hering cc714a
 	if (par->synchronous_fb)
Olaf Hering cc714a
-		synthvid_update(p);
Olaf Hering cc714a
+		synthvid_update(p, 0, 0, INT_MAX, INT_MAX);
Olaf Hering cc714a
+	else
Olaf Hering cc714a
+		hvfb_ondemand_refresh_throttle(par, image->dx, image->dy,
Olaf Hering cc714a
+					       image->width, image->height);
Olaf Hering cc714a
 }
Olaf Hering cc714a
 
Olaf Hering cc714a
 static struct fb_ops hvfb_ops = {
Olaf Hering cc714a
@@ -811,6 +951,9 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info)
Olaf Hering cc714a
 	resource_size_t pot_start, pot_end;
Olaf Hering cc714a
 	int ret;
Olaf Hering cc714a
 
Olaf Hering cc714a
+	dio_fb_size =
Olaf Hering cc714a
+		screen_width * screen_height * screen_depth / 8;
Olaf Hering cc714a
+
Olaf Hering cc714a
 	if (gen2vm) {
Olaf Hering cc714a
 		pot_start = 0;
Olaf Hering cc714a
 		pot_end = -1;
Olaf Hering cc714a
@@ -845,9 +988,14 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info)
Olaf Hering cc714a
 	if (!fb_virt)
Olaf Hering cc714a
 		goto err2;
Olaf Hering cc714a
 
Olaf Hering cc714a
+	/* Allocate memory for deferred IO */
Olaf Hering cc714a
+	par->dio_vp = vzalloc(round_up(dio_fb_size, PAGE_SIZE));
Olaf Hering cc714a
+	if (par->dio_vp == NULL)
Olaf Hering cc714a
+		goto err3;
Olaf Hering cc714a
+
Olaf Hering cc714a
 	info->apertures = alloc_apertures(1);
Olaf Hering cc714a
 	if (!info->apertures)
Olaf Hering cc714a
-		goto err3;
Olaf Hering cc714a
+		goto err4;
Olaf Hering cc714a
 
Olaf Hering cc714a
 	if (gen2vm) {
Olaf Hering cc714a
 		info->apertures->ranges[0].base = screen_info.lfb_base;
Olaf Hering cc714a
@@ -859,16 +1007,23 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info)
Olaf Hering cc714a
 		info->apertures->ranges[0].size = pci_resource_len(pdev, 0);
Olaf Hering cc714a
 	}
Olaf Hering cc714a
 
Olaf Hering cc714a
+	/* Physical address of FB device */
Olaf Hering cc714a
+	par->mmio_pp = par->mem->start;
Olaf Hering cc714a
+	/* Virtual address of FB device */
Olaf Hering cc714a
+	par->mmio_vp = (unsigned char *) fb_virt;
Olaf Hering cc714a
+
Olaf Hering cc714a
 	info->fix.smem_start = par->mem->start;
Olaf Hering cc714a
-	info->fix.smem_len = screen_fb_size;
Olaf Hering cc714a
-	info->screen_base = fb_virt;
Olaf Hering cc714a
-	info->screen_size = screen_fb_size;
Olaf Hering cc714a
+	info->fix.smem_len = dio_fb_size;
Olaf Hering cc714a
+	info->screen_base = par->dio_vp;
Olaf Hering cc714a
+	info->screen_size = dio_fb_size;
Olaf Hering cc714a
 
Olaf Hering cc714a
 	if (!gen2vm)
Olaf Hering cc714a
 		pci_dev_put(pdev);
Olaf Hering cc714a
 
Olaf Hering cc714a
 	return 0;
Olaf Hering cc714a
 
Olaf Hering cc714a
+err4:
Olaf Hering cc714a
+	vfree(par->dio_vp);
Olaf Hering cc714a
 err3:
Olaf Hering cc714a
 	iounmap(fb_virt);
Olaf Hering cc714a
 err2:
Olaf Hering cc714a
@@ -886,6 +1041,7 @@ static void hvfb_putmem(struct fb_info *info)
Olaf Hering cc714a
 {
Olaf Hering cc714a
 	struct hvfb_par *par = info->par;
Olaf Hering cc714a
 
Olaf Hering cc714a
+	vfree(par->dio_vp);
Olaf Hering cc714a
 	iounmap(info->screen_base);
Olaf Hering cc714a
 	vmbus_free_mmio(par->mem->start, screen_fb_size);
Olaf Hering cc714a
 	par->mem = NULL;
Olaf Hering cc714a
@@ -909,6 +1065,11 @@ static int hvfb_probe(struct hv_device *hdev,
Olaf Hering cc714a
 	init_completion(&par->wait);
Olaf Hering cc714a
 	INIT_DELAYED_WORK(&par->dwork, hvfb_update_work);
Olaf Hering cc714a
 
Olaf Hering cc714a
+	par->delayed_refresh = false;
Olaf Hering cc714a
+	spin_lock_init(&par->delayed_refresh_lock);
Olaf Hering cc714a
+	par->x1 = par->y1 = INT_MAX;
Olaf Hering cc714a
+	par->x2 = par->y2 = 0;
Olaf Hering cc714a
+
Olaf Hering cc714a
 	/* Connect to VSP */
Olaf Hering cc714a
 	hv_set_drvdata(hdev, info);
Olaf Hering cc714a
 	ret = synthvid_connect_vsp(hdev);
Olaf Hering cc714a
@@ -960,6 +1121,10 @@ static int hvfb_probe(struct hv_device *hdev,
Olaf Hering cc714a
 	info->fbops = &hvfb_ops;
Olaf Hering cc714a
 	info->pseudo_palette = par->pseudo_palette;
Olaf Hering cc714a
 
Olaf Hering cc714a
+	/* Initialize deferred IO */
Olaf Hering cc714a
+	info->fbdefio = &synthvid_defio;
Olaf Hering cc714a
+	fb_deferred_io_init(info);
Olaf Hering cc714a
+
Olaf Hering cc714a
 	/* Send config to host */
Olaf Hering cc714a
 	ret = synthvid_send_config(hdev);
Olaf Hering cc714a
 	if (ret)
Olaf Hering cc714a
@@ -981,6 +1146,7 @@ static int hvfb_probe(struct hv_device *hdev,
Olaf Hering cc714a
 	return 0;
Olaf Hering cc714a
 
Olaf Hering cc714a
 error:
Olaf Hering cc714a
+	fb_deferred_io_cleanup(info);
Olaf Hering cc714a
 	hvfb_putmem(info);
Olaf Hering cc714a
 error2:
Olaf Hering cc714a
 	vmbus_close(hdev->channel);
Olaf Hering cc714a
@@ -1003,6 +1169,8 @@ static int hvfb_remove(struct hv_device *hdev)
Olaf Hering cc714a
 	par->update = false;
Olaf Hering cc714a
 	par->fb_ready = false;
Olaf Hering cc714a
 
Olaf Hering cc714a
+	fb_deferred_io_cleanup(info);
Olaf Hering cc714a
+
Olaf Hering cc714a
 	unregister_framebuffer(info);
Olaf Hering cc714a
 	cancel_delayed_work_sync(&par->dwork);
Olaf Hering cc714a