drm/cirrus: deal with bo reserve fail in dirty update path
authorDave Airlie <airlied@redhat.com>
Thu, 2 May 2013 06:45:02 +0000 (02:45 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 11 May 2013 20:53:59 +0000 (13:53 -0700)
commit f3b2bbdc8a87a080ccd23d27fca4b87d61340dd4 upstream.

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.

Signed-off-by: Dave Airlie <airlied@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/gpu/drm/cirrus/cirrus_drv.h
drivers/gpu/drm/cirrus/cirrus_fbdev.c
drivers/gpu/drm/cirrus/cirrus_ttm.c

index 6e0cc724e5a23635c8f82fbf72b40b442caf42ef..7ca05959688748a436eb27ae7215ec106dd220d2 100644 (file)
@@ -154,6 +154,8 @@ struct cirrus_fbdev {
        struct list_head fbdev_list;
        void *sysram;
        int size;
+       int x1, y1, x2, y2; /* dirty rect */
+       spinlock_t dirty_lock;
 };
 
 struct cirrus_bo {
index 6c6b4c87d309ca03d9372aff618e2f6cf7e923a7..1e64d6f067b73aa473fa45ada951a101f8f9b954 100644 (file)
@@ -26,16 +26,51 @@ static void cirrus_dirty_update(struct cirrus_fbdev *afbdev,
        int bpp = (afbdev->gfb.base.bits_per_pixel + 7)/8;
        int ret;
        bool unmap = false;
+       bool store_for_later = false;
+       int x2, y2;
+       unsigned long flags;
 
        obj = afbdev->gfb.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);
        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) {
@@ -282,6 +317,7 @@ int cirrus_fbdev_init(struct cirrus_device *cdev)
 
        cdev->mode_info.gfbdev = gfbdev;
        gfbdev->helper.funcs = &cirrus_fb_helper_funcs;
+       spin_lock_init(&gfbdev->dirty_lock);
 
        ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper,
                                 cdev->num_crtc, CIRRUSFB_CONN_LIMIT);
index 1413a26e490527ea69dea371bb3bbc25d92020e2..2ed8cfc740c9fa1845d86edcd876644b69302f82 100644 (file)
@@ -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);
        if (ret) {
-               if (ret != -ERESTARTSYS)
+               if (ret != -ERESTARTSYS && ret != -EBUSY)
                        DRM_ERROR("reserve failed %p\n", bo);
                return ret;
        }