[PATCH 8/8] drm/radeon: rework the VM code a bit more
Jerome Glisse
j.glisse at gmail.com
Wed Sep 12 06:54:32 PDT 2012
On Wed, Sep 12, 2012 at 8:08 AM, Christian König
<deathsimple at vodafone.de> wrote:
> On 11.09.2012 18:11, Jerome Glisse wrote:
>>
>> On Tue, Sep 11, 2012 at 10:10 AM, Christian König
>> <deathsimple at vodafone.de> wrote:
>>>
>>> Roughly based on how nouveau is handling it. Instead of
>>> adding the bo_va when the address is set add the bo_va
>>> when the handle is opened, but set the address to zero
>>> until userspace tells us where to place it.
>>>
>>> This fixes another bunch of problems with glamor.
>>
>> What exactly are the fix ? I don't see why this change is needed. It
>> make things more complicated in my view.
>
> Applications can open GEM Handles multiple times, so what happens is that
> (for example) the DDX creates an BO to back a pixmap, hands that over to
> GLAMOR and than closes the handle again (a bit later and also not all the
> times).
>
> In the end the kernel complains that bo x isn't in vm y, what makes sense
> cause the DDX has removed the mapping by closing the handle. What we don't
> have in this picture is that the handle was opened two times, once for
> creation and once for handing it over to GLAMOR.
>
> I see three possible solutions to that problem:
> 1. Remember how many times a handle was opened in a vm context, that is what
> this patch does.
>
> 2. Instead of removing the mapping on closing the handle we change usespace
> to remove the mapping separately.
>
> 3. Change GEM to only call the open/close callbacks when the handle is
> opened and closed for the first/last time in a process context, that would
> also eliminate the refcounting nouveau is currently doing.
>
> Feel free to choose one, I just have implemented number 1 because that was
> the simplest way to go (for me).
>
> Cheers,
> Christian.
I am all ok for the refcounting part but i don't think the always add
a va to bo is a good idea. It just add more code for no good reason.
Cheers,
Jerome
>
>>
>>> Signed-off-by: Christian König <deathsimple at vodafone.de>
>>> ---
>>> drivers/gpu/drm/radeon/radeon.h | 30 ++++---
>>> drivers/gpu/drm/radeon/radeon_gart.c | 147
>>> ++++++++++++++++++++++------------
>>> drivers/gpu/drm/radeon/radeon_gem.c | 47 +++++++++--
>>> 3 files changed, 153 insertions(+), 71 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/radeon/radeon.h
>>> b/drivers/gpu/drm/radeon/radeon.h
>>> index 8cca1d2..4d67f0f 100644
>>> --- a/drivers/gpu/drm/radeon/radeon.h
>>> +++ b/drivers/gpu/drm/radeon/radeon.h
>>> @@ -292,17 +292,20 @@ struct radeon_mman {
>>>
>>> /* bo virtual address in a specific vm */
>>> struct radeon_bo_va {
>>> - /* bo list is protected by bo being reserved */
>>> + /* protected by bo being reserved */
>>> struct list_head bo_list;
>>> - /* vm list is protected by vm mutex */
>>> - struct list_head vm_list;
>>> - /* constant after initialization */
>>> - struct radeon_vm *vm;
>>> - struct radeon_bo *bo;
>>> uint64_t soffset;
>>> uint64_t eoffset;
>>> uint32_t flags;
>>> bool valid;
>>> + unsigned ref_count;
>>
>> unsigned refcount is a recipe for failure, if somehow you over unref
>> then you va will stay alive forever and this overunref will probably
>> go unnoticed.
>>
>>> +
>>> + /* protected by vm mutex */
>>> + struct list_head vm_list;
>>> +
>>> + /* constant after initialization */
>>> + struct radeon_vm *vm;
>>> + struct radeon_bo *bo;
>>> };
>>>
>>> struct radeon_bo {
>>> @@ -1848,14 +1851,15 @@ void radeon_vm_bo_invalidate(struct radeon_device
>>> *rdev,
>>> struct radeon_bo *bo);
>>> struct radeon_bo_va *radeon_vm_bo_find(struct radeon_vm *vm,
>>> struct radeon_bo *bo);
>>> -int radeon_vm_bo_add(struct radeon_device *rdev,
>>> - struct radeon_vm *vm,
>>> - struct radeon_bo *bo,
>>> - uint64_t offset,
>>> - uint32_t flags);
>>> +struct radeon_bo_va *radeon_vm_bo_add(struct radeon_device *rdev,
>>> + struct radeon_vm *vm,
>>> + struct radeon_bo *bo);
>>> +int radeon_vm_bo_set_addr(struct radeon_device *rdev,
>>> + struct radeon_bo_va *bo_va,
>>> + uint64_t offset,
>>> + uint32_t flags);
>>> int radeon_vm_bo_rmv(struct radeon_device *rdev,
>>> - struct radeon_vm *vm,
>>> - struct radeon_bo *bo);
>>> + struct radeon_bo_va *bo_va);
>>>
>>> /* audio */
>>> void r600_audio_update_hdmi(struct work_struct *work);
>>> diff --git a/drivers/gpu/drm/radeon/radeon_gart.c
>>> b/drivers/gpu/drm/radeon/radeon_gart.c
>>> index 2c59491..2f28ff3 100644
>>> --- a/drivers/gpu/drm/radeon/radeon_gart.c
>>> +++ b/drivers/gpu/drm/radeon/radeon_gart.c
>>> @@ -693,51 +693,83 @@ struct radeon_bo_va *radeon_vm_bo_find(struct
>>> radeon_vm *vm,
>>> * @rdev: radeon_device pointer
>>> * @vm: requested vm
>>> * @bo: radeon buffer object
>>> - * @offset: requested offset of the buffer in the VM address space
>>> - * @flags: attributes of pages (read/write/valid/etc.)
>>> *
>>> * Add @bo into the requested vm (cayman+).
>>> - * Add @bo to the list of bos associated with the vm and validate
>>> - * the offset requested within the vm address space.
>>> - * Returns 0 for success, error for failure.
>>> + * Add @bo to the list of bos associated with the vm
>>> + * Returns newly added bo_va or NULL for failure
>>> *
>>> * Object has to be reserved!
>>> */
>>> -int radeon_vm_bo_add(struct radeon_device *rdev,
>>> - struct radeon_vm *vm,
>>> - struct radeon_bo *bo,
>>> - uint64_t offset,
>>> - uint32_t flags)
>>> +struct radeon_bo_va *radeon_vm_bo_add(struct radeon_device *rdev,
>>> + struct radeon_vm *vm,
>>> + struct radeon_bo *bo)
>>> {
>>> - struct radeon_bo_va *bo_va, *tmp;
>>> - struct list_head *head;
>>> - uint64_t size = radeon_bo_size(bo), last_offset = 0;
>>> - unsigned last_pfn;
>>> + struct radeon_bo_va *bo_va;
>>>
>>> bo_va = kzalloc(sizeof(struct radeon_bo_va), GFP_KERNEL);
>>> if (bo_va == NULL) {
>>> - return -ENOMEM;
>>> + return NULL;
>>> }
>>> bo_va->vm = vm;
>>> bo_va->bo = bo;
>>> - bo_va->soffset = offset;
>>> - bo_va->eoffset = offset + size;
>>> - bo_va->flags = flags;
>>> + bo_va->soffset = 0;
>>> + bo_va->eoffset = 0;
>>> + bo_va->flags = 0;
>>> bo_va->valid = false;
>>> + bo_va->ref_count = 1;
>>> INIT_LIST_HEAD(&bo_va->bo_list);
>>> INIT_LIST_HEAD(&bo_va->vm_list);
>>> - /* make sure object fit at this offset */
>>> - if (bo_va->soffset >= bo_va->eoffset) {
>>> - kfree(bo_va);
>>> - return -EINVAL;
>>> - }
>>>
>>> - last_pfn = bo_va->eoffset / RADEON_GPU_PAGE_SIZE;
>>> - if (last_pfn > rdev->vm_manager.max_pfn) {
>>> - kfree(bo_va);
>>> - dev_err(rdev->dev, "va above limit (0x%08X > 0x%08X)\n",
>>> - last_pfn, rdev->vm_manager.max_pfn);
>>> - return -EINVAL;
>>> + mutex_lock(&vm->mutex);
>>> + list_add(&bo_va->vm_list, &vm->va);
>>> + list_add_tail(&bo_va->bo_list, &bo->va);
>>> + mutex_unlock(&vm->mutex);
>>> +
>>> + return bo_va;
>>> +}
>>> +
>>> +/**
>>> + * radeon_vm_bo_set_addr - set bos virtual address inside a vm
>>> + *
>>> + * @rdev: radeon_device pointer
>>> + * @bo_va: bo_va to store the address
>>> + * @soffset: requested offset of the buffer in the VM address space
>>> + * @flags: attributes of pages (read/write/valid/etc.)
>>> + *
>>> + * Set offset of @bo_va (cayman+).
>>> + * Validate and set the offset requested within the vm address space.
>>> + * Returns 0 for success, error for failure.
>>> + *
>>> + * Object has to be reserved!
>>> + */
>>> +int radeon_vm_bo_set_addr(struct radeon_device *rdev,
>>> + struct radeon_bo_va *bo_va,
>>> + uint64_t soffset,
>>> + uint32_t flags)
>>> +{
>>> + uint64_t size = radeon_bo_size(bo_va->bo);
>>> + uint64_t eoffset, last_offset = 0;
>>> + struct radeon_vm *vm = bo_va->vm;
>>> + struct radeon_bo_va *tmp;
>>> + struct list_head *head;
>>> + unsigned last_pfn;
>>> +
>>> + if (soffset) {
>>> + /* make sure object fit at this offset */
>>> + eoffset = soffset + size;
>>> + if (soffset >= eoffset) {
>>> + return -EINVAL;
>>> + }
>>> +
>>> + last_pfn = eoffset / RADEON_GPU_PAGE_SIZE;
>>> + if (last_pfn > rdev->vm_manager.max_pfn) {
>>> + dev_err(rdev->dev, "va above limit (0x%08X >
>>> 0x%08X)\n",
>>> + last_pfn, rdev->vm_manager.max_pfn);
>>> + return -EINVAL;
>>> + }
>>> +
>>> + } else {
>>> + eoffset = last_pfn = 0;
>>> }
>>>
>>> mutex_lock(&vm->mutex);
>>> @@ -758,24 +790,33 @@ int radeon_vm_bo_add(struct radeon_device *rdev,
>>> head = &vm->va;
>>> last_offset = 0;
>>> list_for_each_entry(tmp, &vm->va, vm_list) {
>>> - if (bo_va->soffset >= last_offset && bo_va->eoffset <=
>>> tmp->soffset) {
>>> + if (bo_va == tmp) {
>>> + /* skip over currently modified bo */
>>> + continue;
>>> + }
>>> +
>>> + if (soffset >= last_offset && eoffset <= tmp->soffset) {
>>> /* bo can be added before this one */
>>> break;
>>> }
>>> - if (bo_va->eoffset > tmp->soffset && bo_va->soffset <
>>> tmp->eoffset) {
>>> + if (eoffset > tmp->soffset && soffset < tmp->eoffset) {
>>> /* bo and tmp overlap, invalid offset */
>>> dev_err(rdev->dev, "bo %p va 0x%08X conflict
>>> with (bo %p 0x%08X 0x%08X)\n",
>>> - bo, (unsigned)bo_va->soffset, tmp->bo,
>>> + bo_va->bo, (unsigned)bo_va->soffset,
>>> tmp->bo,
>>> (unsigned)tmp->soffset,
>>> (unsigned)tmp->eoffset);
>>> - kfree(bo_va);
>>> mutex_unlock(&vm->mutex);
>>> return -EINVAL;
>>> }
>>> last_offset = tmp->eoffset;
>>> head = &tmp->vm_list;
>>> }
>>> - list_add(&bo_va->vm_list, head);
>>> - list_add_tail(&bo_va->bo_list, &bo->va);
>>> +
>>> + bo_va->soffset = soffset;
>>> + bo_va->eoffset = eoffset;
>>> + bo_va->flags = flags;
>>> + bo_va->valid = false;
>>> + list_move(&bo_va->vm_list, head);
>>> +
>>> mutex_unlock(&vm->mutex);
>>> return 0;
>>> }
>>> @@ -855,6 +896,12 @@ int radeon_vm_bo_update_pte(struct radeon_device
>>> *rdev,
>>> return -EINVAL;
>>> }
>>>
>>> + if (!bo_va->soffset) {
>>> + dev_err(rdev->dev, "bo %p don't has a mapping in vm
>>> %p\n",
>>> + bo, vm);
>>> + return -EINVAL;
>>> + }
>>> +
>>> if ((bo_va->valid && mem) || (!bo_va->valid && mem == NULL))
>>> return 0;
>>>
>>> @@ -921,33 +968,26 @@ int radeon_vm_bo_update_pte(struct radeon_device
>>> *rdev,
>>> * radeon_vm_bo_rmv - remove a bo to a specific vm
>>> *
>>> * @rdev: radeon_device pointer
>>> - * @vm: requested vm
>>> - * @bo: radeon buffer object
>>> + * @bo_va: requested bo_va
>>> *
>>> - * Remove @bo from the requested vm (cayman+).
>>> - * Remove @bo from the list of bos associated with the vm and
>>> - * remove the ptes for @bo in the page table.
>>> + * Remove @bo_va->bo from the requested vm (cayman+).
>>> + * Remove @bo_va->bo from the list of bos associated with the bo_va->vm
>>> and
>>> + * remove the ptes for @bo_va in the page table.
>>> * Returns 0 for success.
>>> *
>>> * Object have to be reserved!
>>> */
>>> int radeon_vm_bo_rmv(struct radeon_device *rdev,
>>> - struct radeon_vm *vm,
>>> - struct radeon_bo *bo)
>>> + struct radeon_bo_va *bo_va)
>>> {
>>> - struct radeon_bo_va *bo_va;
>>> int r;
>>>
>>> - bo_va = radeon_vm_bo_find(vm, bo);
>>> - if (bo_va == NULL)
>>> - return 0;
>>> -
>>> mutex_lock(&rdev->vm_manager.lock);
>>> - mutex_lock(&vm->mutex);
>>> - r = radeon_vm_bo_update_pte(rdev, vm, bo, NULL);
>>> + mutex_lock(&bo_va->vm->mutex);
>>> + r = radeon_vm_bo_update_pte(rdev, bo_va->vm, bo_va->bo, NULL);
>>> mutex_unlock(&rdev->vm_manager.lock);
>>> list_del(&bo_va->vm_list);
>>> - mutex_unlock(&vm->mutex);
>>> + mutex_unlock(&bo_va->vm->mutex);
>>> list_del(&bo_va->bo_list);
>>>
>>> kfree(bo_va);
>>> @@ -987,6 +1027,7 @@ void radeon_vm_bo_invalidate(struct radeon_device
>>> *rdev,
>>> */
>>> int radeon_vm_init(struct radeon_device *rdev, struct radeon_vm *vm)
>>> {
>>> + struct radeon_bo_va *bo_va;
>>> int r;
>>>
>>> vm->id = 0;
>>> @@ -1006,8 +1047,10 @@ int radeon_vm_init(struct radeon_device *rdev,
>>> struct radeon_vm *vm)
>>> /* map the ib pool buffer at 0 in virtual address space, set
>>> * read only
>>> */
>>> - r = radeon_vm_bo_add(rdev, vm, rdev->ring_tmp_bo.bo,
>>> RADEON_VA_IB_OFFSET,
>>> - RADEON_VM_PAGE_READABLE |
>>> RADEON_VM_PAGE_SNOOPED);
>>> + bo_va = radeon_vm_bo_add(rdev, vm, rdev->ring_tmp_bo.bo);
>>> + r = radeon_vm_bo_set_addr(rdev, bo_va, RADEON_VA_IB_OFFSET,
>>> + RADEON_VM_PAGE_READABLE |
>>> + RADEON_VM_PAGE_SNOOPED);
>>> return r;
>>> }
>>>
>>> diff --git a/drivers/gpu/drm/radeon/radeon_gem.c
>>> b/drivers/gpu/drm/radeon/radeon_gem.c
>>> index cfad667..6579bef 100644
>>> --- a/drivers/gpu/drm/radeon/radeon_gem.c
>>> +++ b/drivers/gpu/drm/radeon/radeon_gem.c
>>> @@ -124,6 +124,30 @@ void radeon_gem_fini(struct radeon_device *rdev)
>>> */
>>> int radeon_gem_object_open(struct drm_gem_object *obj, struct drm_file
>>> *file_priv)
>>> {
>>> + struct radeon_bo *rbo = gem_to_radeon_bo(obj);
>>> + struct radeon_device *rdev = rbo->rdev;
>>> + struct radeon_fpriv *fpriv = file_priv->driver_priv;
>>> + struct radeon_vm *vm = &fpriv->vm;
>>> + struct radeon_bo_va *bo_va;
>>> + int r;
>>> +
>>> + if (rdev->family < CHIP_CAYMAN) {
>>> + return 0;
>>> + }
>>> +
>>> + r = radeon_bo_reserve(rbo, false);
>>> + if (r) {
>>> + return r;
>>> + }
>>> +
>>> + bo_va = radeon_vm_bo_find(vm, rbo);
>>> + if (!bo_va) {
>>> + bo_va = radeon_vm_bo_add(rdev, vm, rbo);
>>> + } else {
>>> + ++bo_va->ref_count;
>>> + }
>>> + radeon_bo_unreserve(rbo);
>>> +
>>> return 0;
>>> }
>>>
>>> @@ -134,6 +158,7 @@ void radeon_gem_object_close(struct drm_gem_object
>>> *obj,
>>> struct radeon_device *rdev = rbo->rdev;
>>> struct radeon_fpriv *fpriv = file_priv->driver_priv;
>>> struct radeon_vm *vm = &fpriv->vm;
>>> + struct radeon_bo_va *bo_va;
>>> int r;
>>>
>>> if (rdev->family < CHIP_CAYMAN) {
>>> @@ -146,7 +171,12 @@ void radeon_gem_object_close(struct drm_gem_object
>>> *obj,
>>> "we fail to reserve bo (%d)\n", r);
>>> return;
>>> }
>>> - radeon_vm_bo_rmv(rdev, vm, rbo);
>>> + bo_va = radeon_vm_bo_find(vm, rbo);
>>> + if (bo_va) {
>>> + if (--bo_va->ref_count == 0) {
>>> + radeon_vm_bo_rmv(rdev, bo_va);
>>> + }
>>> + }
>>> radeon_bo_unreserve(rbo);
>>> }
>>>
>>> @@ -462,19 +492,24 @@ int radeon_gem_va_ioctl(struct drm_device *dev,
>>> void *data,
>>> drm_gem_object_unreference_unlocked(gobj);
>>> return r;
>>> }
>>> + bo_va = radeon_vm_bo_find(&fpriv->vm, rbo);
>>> + if (!bo_va) {
>>> + args->operation = RADEON_VA_RESULT_ERROR;
>>> + drm_gem_object_unreference_unlocked(gobj);
>>> + return -ENOENT;
>>> + }
>>> +
>>> switch (args->operation) {
>>> case RADEON_VA_MAP:
>>> - bo_va = radeon_vm_bo_find(&fpriv->vm, rbo);
>>> - if (bo_va) {
>>> + if (bo_va->soffset) {
>>> args->operation = RADEON_VA_RESULT_VA_EXIST;
>>> args->offset = bo_va->soffset;
>>> goto out;
>>> }
>>> - r = radeon_vm_bo_add(rdev, &fpriv->vm, rbo,
>>> - args->offset, args->flags);
>>> + r = radeon_vm_bo_set_addr(rdev, bo_va, args->offset,
>>> args->flags);
>>> break;
>>> case RADEON_VA_UNMAP:
>>> - r = radeon_vm_bo_rmv(rdev, &fpriv->vm, rbo);
>>> + r = radeon_vm_bo_set_addr(rdev, bo_va, 0, 0);
>>> break;
>>> default:
>>> break;
>>> --
>>> 1.7.9.5
>>>
>>> _______________________________________________
>>> 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