[PATCH] drm/ttm: Remove set_need_resched from the ttm fault handler

Jakob Bornecrantz wallbraker at gmail.com
Mon Nov 18 07:07:49 PST 2013


On Thu, Nov 14, 2013 at 7:49 PM, Thomas Hellstrom <thellstrom at vmware.com> wrote:
> Addresses
> "[BUG] completely bonkers use of set_need_resched + VM_FAULT_NOPAGE".
>
> In the first occurence it was used to try to be nice while releasing the
> mmap_sem and retrying the fault to work around a locking inversion.
> The second occurence was never used.
>
> There has been some discussion whether we should change the locking order to
> mmap_sem -> bo_reserve. This patch doesn't address that issue, and leaves
> that locking order undefined. The solution that we release the mmap_sem if
> tryreserve fails and wait for the buffer to become unreserved is something
> we want in any case, and follows how the core vm system waits for pages
> to be come unlocked while releasing the mmap_sem.
>
> The code also outlines what needs to be changed if we want to establish the
> locking order as mmap_sem -> bo::reserve.
>
> One slight issue that remains with this code is that the fault handler might
> be prone to starvation if another thread countinously reserves the buffer.
> IMO that usage pattern is highly unlikely.
>
> Signed-off-by: Thomas Hellstrom <thellstrom at vmware.com>

Reviewed-by: Jakob Bornecrantz <jakob at vmware.com>

> ---
>  drivers/gpu/drm/ttm/ttm_bo.c    |   35 ++++++++++++++++++++++++++++++++++-
>  drivers/gpu/drm/ttm/ttm_bo_vm.c |   26 ++++++++++++++++++++------
>  include/drm/ttm/ttm_bo_api.h    |    4 +++-
>  3 files changed, 57 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
> index 8d5a646..07e02c4 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo.c
> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
> @@ -151,7 +151,7 @@ static void ttm_bo_release_list(struct kref *list_kref)
>         atomic_dec(&bo->glob->bo_count);
>         if (bo->resv == &bo->ttm_resv)
>                 reservation_object_fini(&bo->ttm_resv);
> -
> +       mutex_destroy(&bo->wu_mutex);
>         if (bo->destroy)
>                 bo->destroy(bo);
>         else {
> @@ -1123,6 +1123,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev,
>         INIT_LIST_HEAD(&bo->ddestroy);
>         INIT_LIST_HEAD(&bo->swap);
>         INIT_LIST_HEAD(&bo->io_reserve_lru);
> +       mutex_init(&bo->wu_mutex);
>         bo->bdev = bdev;
>         bo->glob = bdev->glob;
>         bo->type = type;
> @@ -1704,3 +1705,35 @@ void ttm_bo_swapout_all(struct ttm_bo_device *bdev)
>                 ;
>  }
>  EXPORT_SYMBOL(ttm_bo_swapout_all);
> +
> +/**
> + * ttm_bo_wait_unreserved - interruptible wait for a buffer object to become
> + * unreserved
> + *
> + * @bo: Pointer to buffer
> + */
> +int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo)
> +{
> +       int ret;
> +
> +       /*
> +        * In the absense of a wait_unlocked API,
> +        * Use the bo::wu_mutex to avoid triggering livelocks due to
> +        * concurrent use of this function. Note that this use of
> +        * bo::wu_mutex can go away if we change locking order to
> +        * mmap_sem -> bo::reserve.
> +        */
> +       ret = mutex_lock_interruptible(&bo->wu_mutex);
> +       if (unlikely(ret != 0))
> +               return -ERESTARTSYS;
> +       if (!ww_mutex_is_locked(&bo->resv->lock))
> +               goto out_unlock;
> +       ret = ttm_bo_reserve_nolru(bo, true, false, false, NULL);
> +       if (unlikely(ret != 0))
> +               goto out_unlock;
> +       ww_mutex_unlock(&bo->resv->lock);
> +
> +out_unlock:
> +       mutex_unlock(&bo->wu_mutex);
> +       return ret;
> +}
> diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c
> index ac617f3..b249ab9 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c
> +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c
> @@ -107,13 +107,28 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
>         /*
>          * Work around locking order reversal in fault / nopfn
>          * between mmap_sem and bo_reserve: Perform a trylock operation
> -        * for reserve, and if it fails, retry the fault after scheduling.
> +        * for reserve, and if it fails, retry the fault after waiting
> +        * for the buffer to become unreserved.
>          */
> -
> -       ret = ttm_bo_reserve(bo, true, true, false, 0);
> +       ret = ttm_bo_reserve(bo, true, true, false, NULL);
>         if (unlikely(ret != 0)) {
> -               if (ret == -EBUSY)
> -                       set_need_resched();
> +               if (ret != -EBUSY)
> +                       return VM_FAULT_NOPAGE;
> +
> +               if (vmf->flags & FAULT_FLAG_ALLOW_RETRY) {
> +                       if (!(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) {
> +                               up_read(&vma->vm_mm->mmap_sem);
> +                               (void) ttm_bo_wait_unreserved(bo);
> +                       }
> +
> +                       return VM_FAULT_RETRY;
> +               }
> +
> +               /*
> +                * If we'd want to change locking order to
> +                * mmap_sem -> bo::reserve, we'd use a blocking reserve here
> +                * instead of retrying the fault...
> +                */
>                 return VM_FAULT_NOPAGE;
>         }
>
> @@ -123,7 +138,6 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
>                 case 0:
>                         break;
>                 case -EBUSY:
> -                       set_need_resched();
>                 case -ERESTARTSYS:
>                         retval = VM_FAULT_NOPAGE;
>                         goto out_unlock;
> diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h
> index 751eaff..ee127ec 100644
> --- a/include/drm/ttm/ttm_bo_api.h
> +++ b/include/drm/ttm/ttm_bo_api.h
> @@ -169,6 +169,7 @@ struct ttm_tt;
>   * @offset: The current GPU offset, which can have different meanings
>   * depending on the memory type. For SYSTEM type memory, it should be 0.
>   * @cur_placement: Hint of current placement.
> + * @wu_mutex: Wait unreserved mutex.
>   *
>   * Base class for TTM buffer object, that deals with data placement and CPU
>   * mappings. GPU mappings are really up to the driver, but for simpler GPUs
> @@ -250,6 +251,7 @@ struct ttm_buffer_object {
>
>         struct reservation_object *resv;
>         struct reservation_object ttm_resv;
> +       struct mutex wu_mutex;
>  };
>
>  /**
> @@ -702,5 +704,5 @@ extern ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp,
>                          size_t count, loff_t *f_pos, bool write);
>
>  extern void ttm_bo_swapout_all(struct ttm_bo_device *bdev);
> -
> +extern int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo);
>  #endif
> --
> 1.7.10.4
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel


More information about the dri-devel mailing list