Merge tag 'iommu-updates-v5.15' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / include / linux / iommu.h
index 9369458..d2f3435 100644 (file)
@@ -40,6 +40,7 @@ struct iommu_domain;
 struct notifier_block;
 struct iommu_sva;
 struct iommu_fault_event;
+struct iommu_dma_cookie;
 
 /* iommu fault flags */
 #define IOMMU_FAULT_READ       0x0
@@ -60,6 +61,7 @@ struct iommu_domain_geometry {
 #define __IOMMU_DOMAIN_DMA_API (1U << 1)  /* Domain for use in DMA-API
                                              implementation              */
 #define __IOMMU_DOMAIN_PT      (1U << 2)  /* Domain is identity mapped   */
+#define __IOMMU_DOMAIN_DMA_FQ  (1U << 3)  /* DMA-API uses flush queue    */
 
 /*
  * This are the possible domain-types
@@ -72,12 +74,17 @@ struct iommu_domain_geometry {
  *     IOMMU_DOMAIN_DMA        - Internally used for DMA-API implementations.
  *                               This flag allows IOMMU drivers to implement
  *                               certain optimizations for these domains
+ *     IOMMU_DOMAIN_DMA_FQ     - As above, but definitely using batched TLB
+ *                               invalidation.
  */
 #define IOMMU_DOMAIN_BLOCKED   (0U)
 #define IOMMU_DOMAIN_IDENTITY  (__IOMMU_DOMAIN_PT)
 #define IOMMU_DOMAIN_UNMANAGED (__IOMMU_DOMAIN_PAGING)
 #define IOMMU_DOMAIN_DMA       (__IOMMU_DOMAIN_PAGING |        \
                                 __IOMMU_DOMAIN_DMA_API)
+#define IOMMU_DOMAIN_DMA_FQ    (__IOMMU_DOMAIN_PAGING |        \
+                                __IOMMU_DOMAIN_DMA_API |       \
+                                __IOMMU_DOMAIN_DMA_FQ)
 
 struct iommu_domain {
        unsigned type;
@@ -86,9 +93,14 @@ struct iommu_domain {
        iommu_fault_handler_t handler;
        void *handler_token;
        struct iommu_domain_geometry geometry;
-       void *iova_cookie;
+       struct iommu_dma_cookie *iova_cookie;
 };
 
+static inline bool iommu_is_dma_domain(struct iommu_domain *domain)
+{
+       return domain->type & __IOMMU_DOMAIN_DMA_API;
+}
+
 enum iommu_cap {
        IOMMU_CAP_CACHE_COHERENCY,      /* IOMMU can enforce cache coherent DMA
                                           transactions */
@@ -160,16 +172,22 @@ enum iommu_dev_features {
  * @start: IOVA representing the start of the range to be flushed
  * @end: IOVA representing the end of the range to be flushed (inclusive)
  * @pgsize: The interval at which to perform the flush
+ * @freelist: Removed pages to free after sync
+ * @queued: Indicates that the flush will be queued
  *
  * This structure is intended to be updated by multiple calls to the
  * ->unmap() function in struct iommu_ops before eventually being passed
- * into ->iotlb_sync().
+ * into ->iotlb_sync(). Drivers can add pages to @freelist to be freed after
+ * ->iotlb_sync() or ->iotlb_flush_all() have cleared all cached references to
+ * them. @queued is set to indicate when ->iotlb_flush_all() will be called
+ * later instead of ->iotlb_sync(), so drivers may optimise accordingly.
  */
 struct iommu_iotlb_gather {
        unsigned long           start;
        unsigned long           end;
        size_t                  pgsize;
        struct page             *freelist;
+       bool                    queued;
 };
 
 /**
@@ -180,7 +198,10 @@ struct iommu_iotlb_gather {
  * @attach_dev: attach device to an iommu domain
  * @detach_dev: detach device from an iommu domain
  * @map: map a physically contiguous memory region to an iommu domain
+ * @map_pages: map a physically contiguous set of pages of the same size to
+ *             an iommu domain.
  * @unmap: unmap a physically contiguous memory region from an iommu domain
+ * @unmap_pages: unmap a number of pages of the same size from an iommu domain
  * @flush_iotlb_all: Synchronously flush all hardware TLBs for this domain
  * @iotlb_sync_map: Sync mappings created recently using @map to the hardware
  * @iotlb_sync: Flush all queued ranges from the hardware TLBs and empty flush
@@ -229,8 +250,14 @@ struct iommu_ops {
        void (*detach_dev)(struct iommu_domain *domain, struct device *dev);
        int (*map)(struct iommu_domain *domain, unsigned long iova,
                   phys_addr_t paddr, size_t size, int prot, gfp_t gfp);
+       int (*map_pages)(struct iommu_domain *domain, unsigned long iova,
+                        phys_addr_t paddr, size_t pgsize, size_t pgcount,
+                        int prot, gfp_t gfp, size_t *mapped);
        size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,
                     size_t size, struct iommu_iotlb_gather *iotlb_gather);
+       size_t (*unmap_pages)(struct iommu_domain *domain, unsigned long iova,
+                             size_t pgsize, size_t pgcount,
+                             struct iommu_iotlb_gather *iotlb_gather);
        void (*flush_iotlb_all)(struct iommu_domain *domain);
        void (*iotlb_sync_map)(struct iommu_domain *domain, unsigned long iova,
                               size_t size);
@@ -476,8 +503,7 @@ int iommu_enable_nesting(struct iommu_domain *domain);
 int iommu_set_pgtable_quirks(struct iommu_domain *domain,
                unsigned long quirks);
 
-void iommu_set_dma_strict(bool val);
-bool iommu_get_dma_strict(struct iommu_domain *domain);
+void iommu_set_dma_strict(void);
 
 extern int report_iommu_fault(struct iommu_domain *domain, struct device *dev,
                              unsigned long iova, int flags);
@@ -497,29 +523,80 @@ static inline void iommu_iotlb_sync(struct iommu_domain *domain,
        iommu_iotlb_gather_init(iotlb_gather);
 }
 
+/**
+ * iommu_iotlb_gather_is_disjoint - Checks whether a new range is disjoint
+ *
+ * @gather: TLB gather data
+ * @iova: start of page to invalidate
+ * @size: size of page to invalidate
+ *
+ * Helper for IOMMU drivers to check whether a new range and the gathered range
+ * are disjoint. For many IOMMUs, flushing the IOMMU in this case is better
+ * than merging the two, which might lead to unnecessary invalidations.
+ */
+static inline
+bool iommu_iotlb_gather_is_disjoint(struct iommu_iotlb_gather *gather,
+                                   unsigned long iova, size_t size)
+{
+       unsigned long start = iova, end = start + size - 1;
+
+       return gather->end != 0 &&
+               (end + 1 < gather->start || start > gather->end + 1);
+}
+
+
+/**
+ * iommu_iotlb_gather_add_range - Gather for address-based TLB invalidation
+ * @gather: TLB gather data
+ * @iova: start of page to invalidate
+ * @size: size of page to invalidate
+ *
+ * Helper for IOMMU drivers to build arbitrarily-sized invalidation commands
+ * where only the address range matters, and simply minimising intermediate
+ * syncs is preferred.
+ */
+static inline void iommu_iotlb_gather_add_range(struct iommu_iotlb_gather *gather,
+                                               unsigned long iova, size_t size)
+{
+       unsigned long end = iova + size - 1;
+
+       if (gather->start > iova)
+               gather->start = iova;
+       if (gather->end < end)
+               gather->end = end;
+}
+
+/**
+ * iommu_iotlb_gather_add_page - Gather for page-based TLB invalidation
+ * @domain: IOMMU domain to be invalidated
+ * @gather: TLB gather data
+ * @iova: start of page to invalidate
+ * @size: size of page to invalidate
+ *
+ * Helper for IOMMU drivers to build invalidation commands based on individual
+ * pages, or with page size/table level hints which cannot be gathered if they
+ * differ.
+ */
 static inline void iommu_iotlb_gather_add_page(struct iommu_domain *domain,
                                               struct iommu_iotlb_gather *gather,
                                               unsigned long iova, size_t size)
 {
-       unsigned long start = iova, end = start + size - 1;
-
        /*
         * If the new page is disjoint from the current range or is mapped at
         * a different granularity, then sync the TLB so that the gather
         * structure can be rewritten.
         */
-       if (gather->pgsize != size ||
-           end + 1 < gather->start || start > gather->end + 1) {
-               if (gather->pgsize)
-                       iommu_iotlb_sync(domain, gather);
-               gather->pgsize = size;
-       }
+       if ((gather->pgsize && gather->pgsize != size) ||
+           iommu_iotlb_gather_is_disjoint(gather, iova, size))
+               iommu_iotlb_sync(domain, gather);
 
-       if (gather->end < end)
-               gather->end = end;
+       gather->pgsize = size;
+       iommu_iotlb_gather_add_range(gather, iova, size);
+}
 
-       if (gather->start > start)
-               gather->start = start;
+static inline bool iommu_iotlb_gather_queued(struct iommu_iotlb_gather *gather)
+{
+       return gather && gather->queued;
 }
 
 /* PCI device grouping function */
@@ -870,6 +947,11 @@ static inline void iommu_iotlb_gather_add_page(struct iommu_domain *domain,
 {
 }
 
+static inline bool iommu_iotlb_gather_queued(struct iommu_iotlb_gather *gather)
+{
+       return false;
+}
+
 static inline void iommu_device_unregister(struct iommu_device *iommu)
 {
 }