drm/ast: deal with bo reserve fail in dirty update path
authorDave Airlie <airlied@redhat.com>
Thu, 2 May 2013 06:40:25 +0000 (02:40 -0400)
committerDave Airlie <airlied@redhat.com>
Thu, 2 May 2013 02:46:47 +0000 (12:46 +1000)
Port over the mgag200 fix to ast 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: Dave Airlie <airlied@redhat.com>
drivers/gpu/drm/ast/ast_drv.h
drivers/gpu/drm/ast/ast_fb.c
drivers/gpu/drm/ast/ast_ttm.c

index 528429252f0f985058d80096d3f5021027cd2751..02e52d543e4b5dc1e55172602c76f783a3a40fef 100644 (file)
@@ -241,6 +241,8 @@ struct ast_fbdev {
        void *sysram;
        int size;
        struct ttm_bo_kmap_obj mapping;
+       int x1, y1, x2, y2; /* dirty rect */
+       spinlock_t dirty_lock;
 };
 
 #define to_ast_crtc(x) container_of(x, struct ast_crtc, base)
index 34931fe7d2c527ccc8ac6b7db926e1504c2aac70..fbc0823cfa18b9dbd35588d07d3c9322c58bc18d 100644 (file)
@@ -53,16 +53,52 @@ static void ast_dirty_update(struct ast_fbdev *afbdev,
        int bpp = (afbdev->afb.base.bits_per_pixel + 7)/8;
        int ret;
        bool unmap = false;
+       bool store_for_later = false;
+       int x2, y2;
+       unsigned long flags;
 
        obj = afbdev->afb.obj;
        bo = gem_to_ast_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 = ast_bo_reserve(bo, true);
        if (ret) {
-               DRM_ERROR("failed to reserve fb bo\n");
+               if (ret != -EBUSY)
+                       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) {
                ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
                if (ret) {
@@ -72,10 +108,10 @@ static void ast_dirty_update(struct ast_fbdev *afbdev,
                }
                unmap = true;
        }
-       for (i = y; i < y + height; i++) {
+       for (i = y; i <= y2; i++) {
                /* assume equal stride for now */
                src_offset = dst_offset = i * afbdev->afb.base.pitches[0] + (x * bpp);
-               memcpy_toio(bo->kmap.virtual + src_offset, afbdev->sysram + src_offset, width * bpp);
+               memcpy_toio(bo->kmap.virtual + src_offset, afbdev->sysram + src_offset, (x2 - x + 1) * bpp);
 
        }
        if (unmap)
@@ -292,6 +328,7 @@ int ast_fbdev_init(struct drm_device *dev)
 
        ast->fbdev = afbdev;
        afbdev->helper.funcs = &ast_fb_helper_funcs;
+       spin_lock_init(&afbdev->dirty_lock);
        ret = drm_fb_helper_init(dev, &afbdev->helper,
                                 1, 1);
        if (ret) {
index 3602731a6112244aa59f6a8b91eec87d0f8adbf8..09da3393c527188f85a5e5f42316c502788e1a93 100644 (file)
@@ -316,7 +316,7 @@ int ast_bo_reserve(struct ast_bo *bo, bool no_wait)
 
        ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0);
        if (ret) {
-               if (ret != -ERESTARTSYS)
+               if (ret != -ERESTARTSYS && ret != -EBUSY)
                        DRM_ERROR("reserve failed %p\n", bo);
                return ret;
        }