Merge tag 'drm-next-2022-05-25' of git://anongit.freedesktop.org/drm/drm
[linux-2.6-microblaze.git] / drivers / iommu / intel / iommu.c
index 2200e3c..ba9a63c 100644 (file)
@@ -1588,7 +1588,8 @@ static void iommu_flush_iotlb_psi(struct intel_iommu *iommu,
                                  unsigned long pfn, unsigned int pages,
                                  int ih, int map)
 {
-       unsigned int mask = ilog2(__roundup_pow_of_two(pages));
+       unsigned int aligned_pages = __roundup_pow_of_two(pages);
+       unsigned int mask = ilog2(aligned_pages);
        uint64_t addr = (uint64_t)pfn << VTD_PAGE_SHIFT;
        u16 did = domain->iommu_did[iommu->seq_id];
 
@@ -1600,10 +1601,30 @@ static void iommu_flush_iotlb_psi(struct intel_iommu *iommu,
        if (domain_use_first_level(domain)) {
                qi_flush_piotlb(iommu, did, PASID_RID2PASID, addr, pages, ih);
        } else {
+               unsigned long bitmask = aligned_pages - 1;
+
+               /*
+                * PSI masks the low order bits of the base address. If the
+                * address isn't aligned to the mask, then compute a mask value
+                * needed to ensure the target range is flushed.
+                */
+               if (unlikely(bitmask & pfn)) {
+                       unsigned long end_pfn = pfn + pages - 1, shared_bits;
+
+                       /*
+                        * Since end_pfn <= pfn + bitmask, the only way bits
+                        * higher than bitmask can differ in pfn and end_pfn is
+                        * by carrying. This means after masking out bitmask,
+                        * high bits starting with the first set bit in
+                        * shared_bits are all equal in both pfn and end_pfn.
+                        */
+                       shared_bits = ~(pfn ^ end_pfn) & ~bitmask;
+                       mask = shared_bits ? __ffs(shared_bits) : BITS_PER_LONG;
+               }
+
                /*
                 * Fallback to domain selective flush if no PSI support or
-                * the size is too big. PSI requires page size to be 2 ^ x,
-                * and the base address is naturally aligned to the size.
+                * the size is too big.
                 */
                if (!cap_pgsel_inv(iommu->cap) ||
                    mask > cap_max_amask_val(iommu->cap))