lkml.org 
[lkml]   [2019]   [Jun]   [11]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v4 8/9] drm/vmwgfx: Implement an infrastructure for read-coherent resources
    Date
    From: Thomas Hellstrom <thellstrom@vmware.com>

    Similar to write-coherent resources, make sure that from the user-space
    point of view, GPU rendered contents is automatically available for
    reading by the CPU.

    Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
    Reviewed-by: Deepak Rawat <drawat@vmware.com>
    ---
    drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 7 +-
    drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c | 73 ++++++++++++-
    drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 103 +++++++++++++++++-
    drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h | 2 +
    drivers/gpu/drm/vmwgfx/vmwgfx_validation.c | 3 +-
    5 files changed, 177 insertions(+), 11 deletions(-)

    diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
    index 9b347923196f..dae3a39bf402 100644
    --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
    +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
    @@ -689,7 +689,8 @@ extern void vmw_resource_unreference(struct vmw_resource **p_res);
    extern struct vmw_resource *vmw_resource_reference(struct vmw_resource *res);
    extern struct vmw_resource *
    vmw_resource_reference_unless_doomed(struct vmw_resource *res);
    -extern int vmw_resource_validate(struct vmw_resource *res, bool intr);
    +extern int vmw_resource_validate(struct vmw_resource *res, bool intr,
    + bool dirtying);
    extern int vmw_resource_reserve(struct vmw_resource *res, bool interruptible,
    bool no_backup);
    extern bool vmw_resource_needs_backup(const struct vmw_resource *res);
    @@ -733,6 +734,8 @@ void vmw_resource_mob_attach(struct vmw_resource *res);
    void vmw_resource_mob_detach(struct vmw_resource *res);
    void vmw_resource_dirty_update(struct vmw_resource *res, pgoff_t start,
    pgoff_t end);
    +int vmw_resources_clean(struct vmw_buffer_object *vbo, pgoff_t start,
    + pgoff_t end, pgoff_t *num_prefault);

    /**
    * vmw_resource_mob_attached - Whether a resource currently has a mob attached
    @@ -1427,6 +1430,8 @@ int vmw_bo_dirty_add(struct vmw_buffer_object *vbo);
    void vmw_bo_dirty_transfer_to_res(struct vmw_resource *res);
    void vmw_bo_dirty_clear_res(struct vmw_resource *res);
    void vmw_bo_dirty_release(struct vmw_buffer_object *vbo);
    +void vmw_bo_dirty_unmap(struct vmw_buffer_object *vbo,
    + pgoff_t start, pgoff_t end);
    vm_fault_t vmw_bo_vm_fault(struct vm_fault *vmf);
    vm_fault_t vmw_bo_vm_mkwrite(struct vm_fault *vmf);

    diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c
    index 8d154f90bdc0..730c51e397dd 100644
    --- a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c
    +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c
    @@ -153,7 +153,6 @@ static void vmw_bo_dirty_scan_mkwrite(struct vmw_buffer_object *vbo)
    }
    }

    -
    /**
    * vmw_bo_dirty_scan - Scan for dirty pages and add them to the dirty
    * tracking structure
    @@ -171,6 +170,51 @@ void vmw_bo_dirty_scan(struct vmw_buffer_object *vbo)
    vmw_bo_dirty_scan_mkwrite(vbo);
    }

    +/**
    + * vmw_bo_dirty_pre_unmap - write-protect and pick up dirty pages before
    + * an unmap_mapping_range operation.
    + * @vbo: The buffer object,
    + * @start: First page of the range within the buffer object.
    + * @end: Last page of the range within the buffer object + 1.
    + *
    + * If we're using the _PAGETABLE scan method, we may leak dirty pages
    + * when calling unmap_mapping_range(). This function makes sure we pick
    + * up all dirty pages.
    + */
    +static void vmw_bo_dirty_pre_unmap(struct vmw_buffer_object *vbo,
    + pgoff_t start, pgoff_t end)
    +{
    + struct vmw_bo_dirty *dirty = vbo->dirty;
    + unsigned long offset = drm_vma_node_start(&vbo->base.vma_node);
    + struct address_space *mapping = vbo->base.bdev->dev_mapping;
    +
    + if (dirty->method != VMW_BO_DIRTY_PAGETABLE || start >= end)
    + return;
    +
    + apply_as_wrprotect(mapping, start + offset, end - start);
    + apply_as_clean(mapping, start + offset, end - start, offset,
    + &dirty->bitmap[0], &dirty->start, &dirty->end);
    +}
    +
    +/**
    + * vmw_bo_dirty_unmap - Clear all ptes pointing to a range within a bo
    + * @vbo: The buffer object,
    + * @start: First page of the range within the buffer object.
    + * @end: Last page of the range within the buffer object + 1.
    + *
    + * This is similar to ttm_bo_unmap_virtual_locked() except it takes a subrange.
    + */
    +void vmw_bo_dirty_unmap(struct vmw_buffer_object *vbo,
    + pgoff_t start, pgoff_t end)
    +{
    + unsigned long offset = drm_vma_node_start(&vbo->base.vma_node);
    + struct address_space *mapping = vbo->base.bdev->dev_mapping;
    +
    + vmw_bo_dirty_pre_unmap(vbo, start, end);
    + unmap_shared_mapping_range(mapping, (offset + start) << PAGE_SHIFT,
    + (loff_t) (end - start) << PAGE_SHIFT);
    +}
    +
    /**
    * vmw_bo_dirty_add - Add a dirty-tracking user to a buffer object
    * @vbo: The buffer object
    @@ -389,21 +433,40 @@ vm_fault_t vmw_bo_vm_fault(struct vm_fault *vmf)
    if (ret)
    return ret;

    + num_prefault = (vma->vm_flags & VM_RAND_READ) ? 1 :
    + TTM_BO_VM_NUM_PREFAULT;
    +
    + if (vbo->dirty) {
    + pgoff_t allowed_prefault;
    + unsigned long page_offset;
    +
    + page_offset = vmf->pgoff - drm_vma_node_start(&bo->vma_node);
    + if (page_offset >= bo->num_pages ||
    + vmw_resources_clean(vbo, page_offset,
    + page_offset + PAGE_SIZE,
    + &allowed_prefault)) {
    + ret = VM_FAULT_SIGBUS;
    + goto out_unlock;
    + }
    +
    + num_prefault = min(num_prefault, allowed_prefault);
    + }
    +
    /*
    - * This will cause mkwrite() to be called for each pte on
    - * write-enable vmas.
    + * If we don't track dirty using the MKWRITE method, make sure
    + * sure the page protection is write-enabled so we don't get
    + * a lot of unnecessary write faults.
    */
    if (vbo->dirty && vbo->dirty->method == VMW_BO_DIRTY_MKWRITE)
    prot = vma->vm_page_prot;
    else
    prot = vm_get_page_prot(vma->vm_flags);

    - num_prefault = (vma->vm_flags & VM_RAND_READ) ? 0 :
    - TTM_BO_VM_NUM_PREFAULT;
    ret = ttm_bo_vm_fault_reserved(vmf, prot, num_prefault);
    if (ret == VM_FAULT_RETRY && !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT))
    return ret;

    +out_unlock:
    reservation_object_unlock(bo->resv);
    return ret;
    }
    diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
    index b84dd5953886..d70ee0df5c13 100644
    --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
    +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
    @@ -395,7 +395,8 @@ static int vmw_resource_buf_alloc(struct vmw_resource *res,
    * should be retried once resources have been freed up.
    */
    static int vmw_resource_do_validate(struct vmw_resource *res,
    - struct ttm_validate_buffer *val_buf)
    + struct ttm_validate_buffer *val_buf,
    + bool dirtying)
    {
    int ret = 0;
    const struct vmw_res_func *func = res->func;
    @@ -437,6 +438,15 @@ static int vmw_resource_do_validate(struct vmw_resource *res,
    * the resource.
    */
    if (res->dirty) {
    + if (dirtying && !res->res_dirty) {
    + pgoff_t start = res->backup_offset >> PAGE_SHIFT;
    + pgoff_t end = __KERNEL_DIV_ROUND_UP
    + (res->backup_offset + res->backup_size,
    + PAGE_SIZE);
    +
    + vmw_bo_dirty_unmap(res->backup, start, end);
    + }
    +
    vmw_bo_dirty_transfer_to_res(res);
    return func->dirty_sync(res);
    }
    @@ -681,6 +691,7 @@ static int vmw_resource_do_evict(struct ww_acquire_ctx *ticket,
    * to the device.
    * @res: The resource to make visible to the device.
    * @intr: Perform waits interruptible if possible.
    + * @dirtying: Pending GPU operation will dirty the resource
    *
    * On succesful return, any backup DMA buffer pointed to by @res->backup will
    * be reserved and validated.
    @@ -690,7 +701,8 @@ static int vmw_resource_do_evict(struct ww_acquire_ctx *ticket,
    * Return: Zero on success, -ERESTARTSYS if interrupted, negative error code
    * on failure.
    */
    -int vmw_resource_validate(struct vmw_resource *res, bool intr)
    +int vmw_resource_validate(struct vmw_resource *res, bool intr,
    + bool dirtying)
    {
    int ret;
    struct vmw_resource *evict_res;
    @@ -707,7 +719,7 @@ int vmw_resource_validate(struct vmw_resource *res, bool intr)
    if (res->backup)
    val_buf.bo = &res->backup->base;
    do {
    - ret = vmw_resource_do_validate(res, &val_buf);
    + ret = vmw_resource_do_validate(res, &val_buf, dirtying);
    if (likely(ret != -EBUSY))
    break;

    @@ -1007,7 +1019,7 @@ int vmw_resource_pin(struct vmw_resource *res, bool interruptible)
    /* Do we really need to pin the MOB as well? */
    vmw_bo_pin_reserved(vbo, true);
    }
    - ret = vmw_resource_validate(res, interruptible);
    + ret = vmw_resource_validate(res, interruptible, true);
    if (vbo)
    ttm_bo_unreserve(&vbo->base);
    if (ret)
    @@ -1082,3 +1094,86 @@ void vmw_resource_dirty_update(struct vmw_resource *res, pgoff_t start,
    res->func->dirty_range_add(res, start << PAGE_SHIFT,
    end << PAGE_SHIFT);
    }
    +
    +/**
    + * vmw_resources_clean - Clean resources intersecting a mob range
    + * @vbo: The mob buffer object
    + * @start: The mob page offset starting the range
    + * @end: The mob page offset ending the range
    + * @num_prefault: Returns how many pages including the first have been
    + * cleaned and are ok to prefault
    + */
    +int vmw_resources_clean(struct vmw_buffer_object *vbo, pgoff_t start,
    + pgoff_t end, pgoff_t *num_prefault)
    +{
    + struct rb_node *cur = vbo->res_tree.rb_node;
    + struct vmw_resource *found = NULL;
    + unsigned long res_start = start << PAGE_SHIFT;
    + unsigned long res_end = end << PAGE_SHIFT;
    + unsigned long last_cleaned = 0;
    +
    + /*
    + * Find the resource with lowest backup_offset that intersects the
    + * range.
    + */
    + while (cur) {
    + struct vmw_resource *cur_res =
    + container_of(cur, struct vmw_resource, mob_node);
    +
    + if (cur_res->backup_offset >= res_end) {
    + cur = cur->rb_left;
    + } else if (cur_res->backup_offset + cur_res->backup_size <=
    + res_start) {
    + cur = cur->rb_right;
    + } else {
    + found = cur_res;
    + cur = cur->rb_left;
    + /* Continue to look for resources with lower offsets */
    + }
    + }
    +
    + /*
    + * In order of increasing backup_offset, clean dirty resorces
    + * intersecting the range.
    + */
    + while (found) {
    + if (found->res_dirty) {
    + int ret;
    +
    + if (!found->func->clean)
    + return -EINVAL;
    +
    + ret = found->func->clean(found);
    + if (ret)
    + return ret;
    +
    + found->res_dirty = false;
    + }
    + last_cleaned = found->backup_offset + found->backup_size;
    + cur = rb_next(&found->mob_node);
    + if (!cur)
    + break;
    +
    + found = container_of(cur, struct vmw_resource, mob_node);
    + if (found->backup_offset >= res_end)
    + break;
    + }
    +
    + /*
    + * Set number of pages allowed prefaulting and fence the buffer object
    + */
    + *num_prefault = 1;
    + if (last_cleaned > res_start) {
    + struct ttm_buffer_object *bo = &vbo->base;
    +
    + *num_prefault = __KERNEL_DIV_ROUND_UP(last_cleaned - res_start,
    + PAGE_SIZE);
    + vmw_bo_fence_single(bo, NULL);
    + if (bo->moving)
    + dma_fence_put(bo->moving);
    + bo->moving = dma_fence_get
    + (reservation_object_get_excl(bo->resv));
    + }
    +
    + return 0;
    +}
    diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h
    index c85144286cfe..3b7438b2d289 100644
    --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h
    +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h
    @@ -77,6 +77,7 @@ struct vmw_user_resource_conv {
    * @dirty_sync: Upload the dirty mob contents to the resource.
    * @dirty_add_range: Add a sequential dirty range to the resource
    * dirty tracker.
    + * @clean: Clean the resource.
    */
    struct vmw_res_func {
    enum vmw_res_type res_type;
    @@ -101,6 +102,7 @@ struct vmw_res_func {
    int (*dirty_sync)(struct vmw_resource *res);
    void (*dirty_range_add)(struct vmw_resource *res, size_t start,
    size_t end);
    + int (*clean)(struct vmw_resource *res);
    };

    /**
    diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c
    index 71349a7bae90..9aaf807ed73c 100644
    --- a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c
    +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c
    @@ -641,7 +641,8 @@ int vmw_validation_res_validate(struct vmw_validation_context *ctx, bool intr)
    struct vmw_resource *res = val->res;
    struct vmw_buffer_object *backup = res->backup;

    - ret = vmw_resource_validate(res, intr);
    + ret = vmw_resource_validate(res, intr, val->dirty_set &&
    + val->dirty);
    if (ret) {
    if (ret != -ERESTARTSYS)
    DRM_ERROR("Failed to validate resource.\n");
    --
    2.20.1
    \
     
     \ /
      Last update: 2019-06-11 14:35    [W:3.246 / U:1.940 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site