video: tegra: nvmap: Check if map_iovmm_area succeeds or not
Hiroshi Doyu [Tue, 26 Feb 2013 11:23:41 +0000 (13:23 +0200)]
Check if mapping succeeds or not and return err if any for client to retry.

bug 1235233
bug 1263718

Change-Id: I1f3b8432e0459c6cb7ae7d265d635e3037040414
Signed-off-by: Hiroshi Doyu <hdoyu@nvidia.com>
Reviewed-on: http://git-master/r/204173
(cherry picked from commit 6cf340b30cc5579bab9d9c0f8b9114b75caa0266)
Reviewed-on: http://git-master/r/215466
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Tested-by: Sandeep Shinde <sashinde@nvidia.com>

arch/arm/mach-tegra/include/mach/iovmm.h
arch/arm/mach-tegra/iovmm.c
drivers/video/tegra/nvmap/nvmap.c
drivers/video/tegra/nvmap/nvmap_iommu.c

index c19cc83..b37bcd9 100644 (file)
@@ -220,7 +220,7 @@ size_t tegra_iovmm_get_max_free(struct tegra_iovmm_client *client);
  * a specific physical address pfn. I/O VMA should have been created with
  * a NULL tegra_iovmm_area_ops structure.
  */
-void tegra_iovmm_vm_insert_pfn(struct tegra_iovmm_area *area,
+int tegra_iovmm_vm_insert_pfn(struct tegra_iovmm_area *area,
        tegra_iovmm_addr_t vaddr, unsigned long pfn);
 
 /*
@@ -301,9 +301,10 @@ static inline size_t tegra_iovmm_get_max_free(struct tegra_iovmm_client *client)
        return 0;
 }
 
-static inline void tegra_iovmm_vm_insert_pfn(struct tegra_iovmm_area *area,
+static inline int tegra_iovmm_vm_insert_pfn(struct tegra_iovmm_area *area,
        tegra_iovmm_addr_t vaddr, unsigned long pfn)
 {
+       return 0;
 }
 
 static inline struct tegra_iovmm_area *tegra_iovmm_find_area_get(
@@ -364,24 +365,30 @@ static inline void tegra_iovmm_resume(void)
        tegra_iommu_create_vm((c)->dev, i, s, p)
 #define tegra_iovmm_free_vm(v) tegra_iommu_free_vm(v)
 
+#define tegra_iovmm_zap_vm(v)  tegra_iommu_zap_vm(v)
+
 #define tegra_iovmm_get_vm_size(c)     dma_iova_get_free_total((c)->dev)
 #define tegra_iovmm_get_max_free(c)    dma_iova_get_free_max((c)->dev)
 
 #define tegra_iovmm_vm_insert_pfn(area, handle, pfn)                   \
-       do {                                                            \
+       ({                                                              \
+               dma_addr_t da;                                          \
                struct device *dev = area->dev;                         \
                struct dma_map_ops *ops = get_dma_ops(dev);             \
                DEFINE_DMA_ATTRS(attrs);                                \
                dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs);           \
-               ops->map_page_at(dev, pfn_to_page(pfn), handle,         \
+               da = ops->map_page_at(dev, pfn_to_page(pfn), handle,    \
                                 PAGE_SIZE, 0, 0, &attrs);              \
-       } while (0)
+               dma_mapping_error(dev, da);                             \
+       })
 
 struct tegra_iovmm_area *tegra_iommu_create_vm(struct device *dev,
                       dma_addr_t req, size_t size, pgprot_t prot);
 
 void tegra_iommu_free_vm(struct tegra_iovmm_area *area);
 
+void tegra_iommu_zap_vm(struct tegra_iovmm_area *area);
+
 struct tegra_iovmm_client *tegra_iommu_alloc_client(struct device *dev);
 
 void tegra_iommu_free_client(struct tegra_iovmm_client *client);
index b4675d6..e780e81 100644 (file)
@@ -521,7 +521,7 @@ struct tegra_iovmm_area *tegra_iovmm_create_vm(
        return &b->vm_area;
 }
 
-void tegra_iovmm_vm_insert_pfn(struct tegra_iovmm_area *vm,
+int tegra_iovmm_vm_insert_pfn(struct tegra_iovmm_area *vm,
        tegra_iovmm_addr_t vaddr, unsigned long pfn)
 {
        struct tegra_iovmm_domain *domain = vm->domain;
@@ -532,6 +532,7 @@ void tegra_iovmm_vm_insert_pfn(struct tegra_iovmm_area *vm,
        BUG_ON(vm->ops);
 
        domain->dev->ops->map_pfn(domain, vm, vaddr, pfn);
+       return 0;
 }
 
 void tegra_iovmm_zap_vm(struct tegra_iovmm_area *vm)
index 04fca9c..e5d829b 100644 (file)
 #define NVMAP_HANDLE_VISITED (0x1ul << 31)
 
 /* map the backing pages for a heap_pgalloc handle into its IOVMM area */
-static void map_iovmm_area(struct nvmap_handle *h)
+static int map_iovmm_area(struct nvmap_handle *h)
 {
        tegra_iovmm_addr_t va;
        unsigned long i;
+       int err;
 
        BUG_ON(!h->heap_pgalloc || !h->pgalloc.area);
        BUG_ON(h->size & ~PAGE_MASK);
@@ -59,9 +60,17 @@ static void map_iovmm_area(struct nvmap_handle *h)
 
                pfn = page_to_pfn(h->pgalloc.pages[i]);
                BUG_ON(!pfn_valid(pfn));
-               tegra_iovmm_vm_insert_pfn(h->pgalloc.area, va, pfn);
+               err = tegra_iovmm_vm_insert_pfn(h->pgalloc.area, va, pfn);
+               if (err)
+                       goto err_out;
        }
        h->pgalloc.dirty = false;
+
+       return 0;
+
+err_out:
+       tegra_iovmm_zap_vm(h->pgalloc.area);
+       return err;
 }
 
 /* must be called inside nvmap_pin_lock, to ensure that an entire stream
@@ -328,8 +337,11 @@ int nvmap_pin_ids(struct nvmap_client *client,
                ret = -EINTR;
        } else {
                for (i = 0; i < nr; i++) {
-                       if (h[i]->heap_pgalloc && h[i]->pgalloc.dirty)
-                               map_iovmm_area(h[i]);
+                       if (h[i]->heap_pgalloc && h[i]->pgalloc.dirty) {
+                               ret = map_iovmm_area(h[i]);
+                               while (ret && --i >= 0)
+                                       tegra_iovmm_zap_vm(h[i]->pgalloc.area);
+                       }
                }
        }
 
@@ -489,24 +501,36 @@ int nvmap_pin_array(struct nvmap_client *client,
        mutex_unlock(&client->share->pin_lock);
 
        if (WARN_ON(ret)) {
-               for (i = 0; i < count; i++) {
-                       /* pin ref */
-                       nvmap_handle_put(unique_arr[i]);
-                       /* remove duplicate */
-                       atomic_dec(&unique_arr_refs[i]->dupes);
-                       nvmap_handle_put(unique_arr[i]);
-               }
-               return ret;
+               goto err_out;
        } else {
                for (i = 0; i < count; i++) {
                        if (unique_arr[i]->heap_pgalloc &&
-                           unique_arr[i]->pgalloc.dirty)
-                               map_iovmm_area(unique_arr[i]);
+                           unique_arr[i]->pgalloc.dirty) {
+                               ret = map_iovmm_area(unique_arr[i]);
+                               while (ret && --i >= 0) {
+                                       tegra_iovmm_zap_vm(
+                                               unique_arr[i]->pgalloc.area);
+                                       atomic_dec(&unique_arr_refs[i]->pin);
+                               }
+                               if (ret)
+                                       goto err_out;
+                       }
 
                        atomic_inc(&unique_arr_refs[i]->pin);
                }
        }
        return count;
+
+err_out:
+       for (i = 0; i < count; i++) {
+               /* pin ref */
+               nvmap_handle_put(unique_arr[i]);
+               /* remove duplicate */
+               atomic_dec(&unique_arr_refs[i]->dupes);
+               nvmap_handle_put(unique_arr[i]);
+       }
+
+       return ret;
 }
 
 static phys_addr_t handle_phys(struct nvmap_handle *h)
@@ -565,15 +589,21 @@ phys_addr_t _nvmap_pin(struct nvmap_client *client,
        }
 
        if (ret) {
-               atomic_dec(&ref->pin);
-               nvmap_handle_put(h);
+               goto err_out;
        } else {
                if (h->heap_pgalloc && h->pgalloc.dirty)
-                       map_iovmm_area(h);
+                       ret = map_iovmm_area(h);
+               if (ret)
+                       goto err_out;
                phys = handle_phys(h);
        }
 
-       return ret ?: phys;
+       return phys;
+
+err_out:
+       atomic_dec(&ref->pin);
+       nvmap_handle_put(h);
+       return ret;
 }
 
 phys_addr_t nvmap_pin(struct nvmap_client *client,
index f050556..034e567 100644 (file)
@@ -60,6 +60,15 @@ void tegra_iommu_free_vm(struct tegra_iovmm_area *area)
        kfree(area);
 }
 
+void tegra_iommu_zap_vm(struct tegra_iovmm_area *area)
+{
+       DEFINE_DMA_ATTRS(attrs);
+       dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs);
+       dma_set_attr(DMA_ATTR_SKIP_FREE_IOVA, &attrs);
+       dma_unmap_single_attrs(area->dev, area->iovm_start, area->iovm_length,
+                              0, &attrs);
+}
+
 #ifdef CONFIG_PLATFORM_ENABLE_IOMMU
 
 static inline int tegra_iommu_create_map(struct device *dev)