Commit f3b2bbdc authored by Dave Airlie's avatar Dave Airlie

drm/cirrus: deal with bo reserve fail in dirty update path

Port over the mgag200 fix to cirrus as it suffers the same issue.

    On F19 testing, it was noticed we get a lot of errors in dmesg
    about being unable to reserve the buffer when plymouth starts,
    this is due to the buffer being in the process of migrating,
    so it makes sense we can't reserve it.

    In order to deal with it, this adds delayed updates for the dirty
    updates, when the bo is unreservable, in the normal console case
    this shouldn't ever happen, its just when plymouth or X is
    pushing the console bo to system memory.

Cc: stable@vger.kernel.org
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent 306373b6
...@@ -154,6 +154,8 @@ struct cirrus_fbdev { ...@@ -154,6 +154,8 @@ struct cirrus_fbdev {
struct list_head fbdev_list; struct list_head fbdev_list;
void *sysram; void *sysram;
int size; int size;
int x1, y1, x2, y2; /* dirty rect */
spinlock_t dirty_lock;
}; };
struct cirrus_bo { struct cirrus_bo {
......
...@@ -27,16 +27,51 @@ static void cirrus_dirty_update(struct cirrus_fbdev *afbdev, ...@@ -27,16 +27,51 @@ static void cirrus_dirty_update(struct cirrus_fbdev *afbdev,
int bpp = (afbdev->gfb.base.bits_per_pixel + 7)/8; int bpp = (afbdev->gfb.base.bits_per_pixel + 7)/8;
int ret; int ret;
bool unmap = false; bool unmap = false;
bool store_for_later = false;
int x2, y2;
unsigned long flags;
obj = afbdev->gfb.obj; obj = afbdev->gfb.obj;
bo = gem_to_cirrus_bo(obj); bo = gem_to_cirrus_bo(obj);
/*
* try and reserve the BO, if we fail with busy
* then the BO is being moved and we should
* store up the damage until later.
*/
ret = cirrus_bo_reserve(bo, true); ret = cirrus_bo_reserve(bo, true);
if (ret) { if (ret) {
DRM_ERROR("failed to reserve fb bo\n"); if (ret != -EBUSY)
return; return;
store_for_later = true;
} }
x2 = x + width - 1;
y2 = y + height - 1;
spin_lock_irqsave(&afbdev->dirty_lock, flags);
if (afbdev->y1 < y)
y = afbdev->y1;
if (afbdev->y2 > y2)
y2 = afbdev->y2;
if (afbdev->x1 < x)
x = afbdev->x1;
if (afbdev->x2 > x2)
x2 = afbdev->x2;
if (store_for_later) {
afbdev->x1 = x;
afbdev->x2 = x2;
afbdev->y1 = y;
afbdev->y2 = y2;
spin_unlock_irqrestore(&afbdev->dirty_lock, flags);
return;
}
afbdev->x1 = afbdev->y1 = INT_MAX;
afbdev->x2 = afbdev->y2 = 0;
spin_unlock_irqrestore(&afbdev->dirty_lock, flags);
if (!bo->kmap.virtual) { if (!bo->kmap.virtual) {
ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
if (ret) { if (ret) {
...@@ -268,6 +303,7 @@ int cirrus_fbdev_init(struct cirrus_device *cdev) ...@@ -268,6 +303,7 @@ int cirrus_fbdev_init(struct cirrus_device *cdev)
cdev->mode_info.gfbdev = gfbdev; cdev->mode_info.gfbdev = gfbdev;
gfbdev->helper.funcs = &cirrus_fb_helper_funcs; gfbdev->helper.funcs = &cirrus_fb_helper_funcs;
spin_lock_init(&gfbdev->dirty_lock);
ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper, ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper,
cdev->num_crtc, CIRRUSFB_CONN_LIMIT); cdev->num_crtc, CIRRUSFB_CONN_LIMIT);
......
...@@ -321,7 +321,7 @@ int cirrus_bo_reserve(struct cirrus_bo *bo, bool no_wait) ...@@ -321,7 +321,7 @@ int cirrus_bo_reserve(struct cirrus_bo *bo, bool no_wait)
ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0); ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0);
if (ret) { if (ret) {
if (ret != -ERESTARTSYS) if (ret != -ERESTARTSYS && ret != -EBUSY)
DRM_ERROR("reserve failed %p\n", bo); DRM_ERROR("reserve failed %p\n", bo);
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment