summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikulas Patocka <mpatocka@redhat.com>2019-04-01 17:46:56 +0200
committerBartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>2019-04-01 17:46:56 +0200
commit6b11f9d8433b471fdd3ebed232b43a4b723be6ff (patch)
tree0ea2723e1a6b691cba78eda2ebe365305c11b59b
parentbd86b6c5c60711dbd4fa21bdb497a188ecb6cf63 (diff)
udlfb: fix sleeping inside spinlock
If a framebuffer device is used as a console, the rendering calls (copyarea, fillrect, imageblit) may be done with the console spinlock held. On udlfb, these function call dlfb_handle_damage that takes a blocking semaphore before acquiring an URB. In order to fix the bug, this patch changes the calls copyarea, fillrect and imageblit to offload USB work to a workqueue. A side effect of this patch is 3x improvement in console scrolling speed because the device doesn't have to be updated after each copyarea call. Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Cc: Bernie Thompson <bernie@plugable.com> Cc: Ladislav Michl <ladis@linux-mips.org> Cc: <stable@vger.kernel.org> Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
-rw-r--r--drivers/video/fbdev/udlfb.c56
-rw-r--r--include/video/udlfb.h6
2 files changed, 59 insertions, 3 deletions
diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c
index cf4e9928001c..ce30fa710244 100644
--- a/drivers/video/fbdev/udlfb.c
+++ b/drivers/video/fbdev/udlfb.c
@@ -657,6 +657,50 @@ error:
return 0;
}
+static void dlfb_init_damage(struct dlfb_data *dlfb)
+{
+ dlfb->damage_x = INT_MAX;
+ dlfb->damage_x2 = 0;
+ dlfb->damage_y = INT_MAX;
+ dlfb->damage_y2 = 0;
+}
+
+static void dlfb_damage_work(struct work_struct *w)
+{
+ struct dlfb_data *dlfb = container_of(w, struct dlfb_data, damage_work);
+ int x, x2, y, y2;
+
+ spin_lock_irq(&dlfb->damage_lock);
+ x = dlfb->damage_x;
+ x2 = dlfb->damage_x2;
+ y = dlfb->damage_y;
+ y2 = dlfb->damage_y2;
+ dlfb_init_damage(dlfb);
+ spin_unlock_irq(&dlfb->damage_lock);
+
+ if (x < x2 && y < y2)
+ dlfb_handle_damage(dlfb, x, y, x2 - x, y2 - y);
+}
+
+static void dlfb_offload_damage(struct dlfb_data *dlfb, int x, int y, int width, int height)
+{
+ unsigned long flags;
+ int x2 = x + width;
+ int y2 = y + height;
+
+ if (x >= x2 || y >= y2)
+ return;
+
+ spin_lock_irqsave(&dlfb->damage_lock, flags);
+ dlfb->damage_x = min(x, dlfb->damage_x);
+ dlfb->damage_x2 = max(x2, dlfb->damage_x2);
+ dlfb->damage_y = min(y, dlfb->damage_y);
+ dlfb->damage_y2 = max(y2, dlfb->damage_y2);
+ spin_unlock_irqrestore(&dlfb->damage_lock, flags);
+
+ schedule_work(&dlfb->damage_work);
+}
+
/*
* Path triggered by usermode clients who write to filesystem
* e.g. cat filename > /dev/fb1
@@ -693,7 +737,7 @@ static void dlfb_ops_copyarea(struct fb_info *info,
sys_copyarea(info, area);
- dlfb_handle_damage(dlfb, area->dx, area->dy,
+ dlfb_offload_damage(dlfb, area->dx, area->dy,
area->width, area->height);
}
@@ -704,7 +748,7 @@ static void dlfb_ops_imageblit(struct fb_info *info,
sys_imageblit(info, image);
- dlfb_handle_damage(dlfb, image->dx, image->dy,
+ dlfb_offload_damage(dlfb, image->dx, image->dy,
image->width, image->height);
}
@@ -715,7 +759,7 @@ static void dlfb_ops_fillrect(struct fb_info *info,
sys_fillrect(info, rect);
- dlfb_handle_damage(dlfb, rect->dx, rect->dy, rect->width,
+ dlfb_offload_damage(dlfb, rect->dx, rect->dy, rect->width,
rect->height);
}
@@ -940,6 +984,8 @@ static void dlfb_ops_destroy(struct fb_info *info)
{
struct dlfb_data *dlfb = info->par;
+ cancel_work_sync(&dlfb->damage_work);
+
if (info->cmap.len != 0)
fb_dealloc_cmap(&info->cmap);
if (info->monspecs.modedb)
@@ -1636,6 +1682,10 @@ static int dlfb_usb_probe(struct usb_interface *intf,
dlfb->ops = dlfb_ops;
info->fbops = &dlfb->ops;
+ dlfb_init_damage(dlfb);
+ spin_lock_init(&dlfb->damage_lock);
+ INIT_WORK(&dlfb->damage_work, dlfb_damage_work);
+
INIT_LIST_HEAD(&info->modelist);
if (!dlfb_alloc_urb_list(dlfb, WRITES_IN_FLIGHT, MAX_TRANSFER)) {
diff --git a/include/video/udlfb.h b/include/video/udlfb.h
index 7d09e54ae54e..a3724f1fca1c 100644
--- a/include/video/udlfb.h
+++ b/include/video/udlfb.h
@@ -48,6 +48,12 @@ struct dlfb_data {
int base8;
u32 pseudo_palette[256];
int blank_mode; /*one of FB_BLANK_ */
+ int damage_x;
+ int damage_y;
+ int damage_x2;
+ int damage_y2;
+ spinlock_t damage_lock;
+ struct work_struct damage_work;
struct fb_ops ops;
/* blit-only rendering path metrics, exposed through sysfs */
atomic_t bytes_rendered; /* raw pixel-bytes driver asked to render */