Merge tag 'iommu-config-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[cascardo/linux.git] / drivers / iommu / iommu.c
index 02f798b..f7718d7 100644 (file)
@@ -818,7 +818,15 @@ static int iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops)
                kfree(nb);
                return err;
        }
-       return bus_for_each_dev(bus, NULL, &cb, add_iommu_group);
+
+       err = bus_for_each_dev(bus, NULL, &cb, add_iommu_group);
+       if (err) {
+               bus_unregister_notifier(bus, nb);
+               kfree(nb);
+               return err;
+       }
+
+       return 0;
 }
 
 /**
@@ -836,13 +844,19 @@ static int iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops)
  */
 int bus_set_iommu(struct bus_type *bus, const struct iommu_ops *ops)
 {
+       int err;
+
        if (bus->iommu_ops != NULL)
                return -EBUSY;
 
        bus->iommu_ops = ops;
 
        /* Do IOMMU specific setup for this bus-type */
-       return iommu_bus_init(bus, ops);
+       err = iommu_bus_init(bus, ops);
+       if (err)
+               bus->iommu_ops = NULL;
+
+       return err;
 }
 EXPORT_SYMBOL_GPL(bus_set_iommu);
 
@@ -1124,6 +1138,48 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size)
 }
 EXPORT_SYMBOL_GPL(iommu_unmap);
 
+size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
+                        struct scatterlist *sg, unsigned int nents, int prot)
+{
+       struct scatterlist *s;
+       size_t mapped = 0;
+       unsigned int i, min_pagesz;
+       int ret;
+
+       if (unlikely(domain->ops->pgsize_bitmap == 0UL))
+               return 0;
+
+       min_pagesz = 1 << __ffs(domain->ops->pgsize_bitmap);
+
+       for_each_sg(sg, s, nents, i) {
+               phys_addr_t phys = page_to_phys(sg_page(s)) + s->offset;
+
+               /*
+                * We are mapping on IOMMU page boundaries, so offset within
+                * the page must be 0. However, the IOMMU may support pages
+                * smaller than PAGE_SIZE, so s->offset may still represent
+                * an offset of that boundary within the CPU page.
+                */
+               if (!IS_ALIGNED(s->offset, min_pagesz))
+                       goto out_err;
+
+               ret = iommu_map(domain, iova + mapped, phys, s->length, prot);
+               if (ret)
+                       goto out_err;
+
+               mapped += s->length;
+       }
+
+       return mapped;
+
+out_err:
+       /* undo mappings already done */
+       iommu_unmap(domain, iova, mapped);
+
+       return 0;
+
+}
+EXPORT_SYMBOL_GPL(default_iommu_map_sg);
 
 int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr,
                               phys_addr_t paddr, u64 size, int prot)