video: tegra: host: gk20a: Add refcounting to vm
Arto Merilainen [Wed, 30 Oct 2013 13:12:11 +0000 (15:12 +0200)]
We used to release vm immediately after as_share was destroyed.
Usually all hw operations have been finished this point, however, in
rare cases we may not have processed completion of all work on the
hardware.

This patch adds reference counting to vm. We currently take reference
each time we create a vm and make a submit using the vm. Reference is
dropped after submit completion and when as_share releases the vm.

Bug 1374242

Change-Id: I473d1ca06bc883cf30f7da953ca30cd27c046daa
Signed-off-by: Arto Merilainen <amerilainen@nvidia.com>
Reviewed-on: http://git-master/r/309636
Reviewed-by: Mitch Luban <mluban@nvidia.com>
Tested-by: Mitch Luban <mluban@nvidia.com>

drivers/video/tegra/host/gk20a/channel_gk20a.c
drivers/video/tegra/host/gk20a/mm_gk20a.c
drivers/video/tegra/host/gk20a/mm_gk20a.h

index 93d6950..d7562b1 100644 (file)
@@ -1262,13 +1262,19 @@ static int gk20a_channel_add_job(struct channel_gk20a *c,
        struct mapped_buffer_node **mapped_buffers = NULL;
        int err = 0, num_mapped_buffers;
 
+       /* job needs reference to this vm */
+       vm->get(vm);
+
        err = vm->get_buffers(vm, &mapped_buffers, &num_mapped_buffers);
-       if (err)
+       if (err) {
+               vm->put(vm);
                return err;
+       }
 
        job = kzalloc(sizeof(*job), GFP_KERNEL);
        if (!job) {
                vm->put_buffers(vm, mapped_buffers, num_mapped_buffers);
+               vm->put(vm);
                return -ENOMEM;
        }
 
@@ -1299,6 +1305,10 @@ void gk20a_channel_update(struct channel_gk20a *c)
 
                vm->put_buffers(vm, job->mapped_buffers,
                                job->num_mapped_buffers);
+
+               /* job is done. release its reference to vm */
+               vm->put(vm);
+
                list_del_init(&job->list);
                kfree(job);
        }
index 29979a2..67b5517 100644 (file)
@@ -1577,6 +1577,25 @@ void gk20a_vm_remove_support(struct vm_gk20a *vm)
        nvhost_allocator_destroy(&vm->vma[gmmu_page_size_big]);
 
        mutex_unlock(&vm->update_gmmu_lock);
+
+       /* vm is not used anymore. release it. */
+       kfree(vm);
+}
+
+static void gk20a_vm_remove_support_kref(struct kref *ref)
+{
+       struct vm_gk20a *vm = container_of(ref, struct vm_gk20a, ref);
+       vm->remove_support(vm);
+}
+
+static void gk20a_vm_get(struct vm_gk20a *vm)
+{
+       kref_get(&vm->ref);
+}
+
+static void gk20a_vm_put(struct vm_gk20a *vm)
+{
+       kref_put(&vm->ref, gk20a_vm_remove_support_kref);
 }
 
 /* address space interfaces for the gk20a module */
@@ -1676,6 +1695,7 @@ static int gk20a_as_alloc_share(struct nvhost_as_share *as_share)
        vm->mapped_buffers = RB_ROOT;
 
        mutex_init(&vm->update_gmmu_lock);
+       kref_init(&vm->ref);
 
        vm->alloc_va       = gk20a_vm_alloc_va;
        vm->free_va        = gk20a_vm_free_va;
@@ -1687,6 +1707,8 @@ static int gk20a_as_alloc_share(struct nvhost_as_share *as_share)
        vm->tlb_inval      = gk20a_mm_tlb_invalidate;
        vm->find_buffer    = gk20a_vm_find_buffer;
        vm->remove_support = gk20a_vm_remove_support;
+       vm->put            = gk20a_vm_put;
+       vm->get            = gk20a_vm_get;
 
        vm->enable_ctag = true;
 
@@ -1700,10 +1722,12 @@ static int gk20a_as_release_share(struct nvhost_as_share *as_share)
 
        nvhost_dbg_fn("");
 
-       gk20a_vm_remove_support(vm);
+       vm->as_share = NULL;
+
+       /* put as reference to vm */
+       vm->put(vm);
 
        as_share->priv = NULL;
-       kfree(vm);
 
        return 0;
 }
@@ -1994,6 +2018,7 @@ int gk20a_init_bar1_vm(struct mm_gk20a *mm)
        vm->mapped_buffers = RB_ROOT;
 
        mutex_init(&vm->update_gmmu_lock);
+       kref_init(&vm->ref);
 
        vm->alloc_va       = gk20a_vm_alloc_va;
        vm->free_va        = gk20a_vm_free_va;
@@ -2004,6 +2029,8 @@ int gk20a_init_bar1_vm(struct mm_gk20a *mm)
        vm->get_buffers    = gk20a_vm_get_buffers;
        vm->tlb_inval      = gk20a_mm_tlb_invalidate;
        vm->remove_support = gk20a_vm_remove_support;
+       vm->put            = gk20a_vm_put;
+       vm->get            = gk20a_vm_get;
 
        return 0;
 
@@ -2145,6 +2172,7 @@ int gk20a_init_pmu_vm(struct mm_gk20a *mm)
        vm->mapped_buffers = RB_ROOT;
 
        mutex_init(&vm->update_gmmu_lock);
+       kref_init(&vm->ref);
 
        vm->alloc_va       = gk20a_vm_alloc_va;
        vm->free_va        = gk20a_vm_free_va;
@@ -2155,6 +2183,8 @@ int gk20a_init_pmu_vm(struct mm_gk20a *mm)
        vm->get_buffers    = gk20a_vm_get_buffers;
        vm->tlb_inval      = gk20a_mm_tlb_invalidate;
        vm->remove_support = gk20a_vm_remove_support;
+       vm->put            = gk20a_vm_put;
+       vm->get            = gk20a_vm_get;
 
        return 0;
 
index 1f945f0..fc84596 100644 (file)
@@ -172,6 +172,8 @@ struct vm_gk20a {
        bool tlb_dirty;
        bool mapped;
 
+       struct kref ref;
+
        struct mutex update_gmmu_lock;
 
        struct page_directory_gk20a pdes;
@@ -221,6 +223,8 @@ struct vm_gk20a {
                        u64 *offset);
 
        void (*remove_support)(struct vm_gk20a *vm);
+       void (*get)(struct vm_gk20a *vm);
+       void (*put)(struct vm_gk20a *vm);
 };
 
 struct gk20a;