diff --git a/drivers/iommu/generic_pt/iommu_pt.h b/drivers/iommu/generic_pt/iommu_pt.h index 5ec135cf43e2..61c6d79c712c 100644 --- a/drivers/iommu/generic_pt/iommu_pt.h +++ b/drivers/iommu/generic_pt/iommu_pt.h @@ -43,6 +43,8 @@ static void flush_writes_item(const struct pt_state *pts) struct iommupt_pending_gather { struct iommu_iotlb_gather *iotlb_gather; struct iommu_pages_list free_list; + u8 leaf_levels_bitmap; + u8 table_levels_bitmap; }; static void gather_add_table(struct iommupt_pending_gather *pending, @@ -50,6 +52,17 @@ static void gather_add_table(struct iommupt_pending_gather *pending, struct pt_table_p *table) { iommu_pages_list_add(&pending->free_list, table); + if (pts_feature(pts, PT_FEAT_DETAILED_GATHER)) + pending->table_levels_bitmap |= BIT(pts->level); +} + +static void gather_add_leaf(struct iommupt_pending_gather *pending, + const struct pt_state *pts) +{ + if (!pts_feature(pts, PT_FEAT_DETAILED_GATHER)) + return; + + pending->leaf_levels_bitmap |= BIT(pts->level); } static void gather_range_pending(struct iommupt_pending_gather *pending, @@ -86,6 +99,15 @@ static void gather_range_pending(struct iommupt_pending_gather *pending, iommu_pages_list_splice(&pending->free_list, &iotlb_gather->freelist); INIT_LIST_HEAD(&pending->free_list.pages); + + if (pt_feature(common, PT_FEAT_DETAILED_GATHER)) { + iotlb_gather->pt.leaf_levels_bitmap |= + pending->leaf_levels_bitmap; + iotlb_gather->pt.table_levels_bitmap |= + pending->table_levels_bitmap; + pending->leaf_levels_bitmap = 0; + pending->table_levels_bitmap = 0; + } } #define DOMAIN_NS(op) CONCATENATE(CONCATENATE(pt_iommu_, PTPFX), op) @@ -1059,6 +1081,7 @@ static __maybe_unused int __unmap_range(struct pt_range *range, void *arg, */ num_contig_lg2 = pt_entry_num_contig_lg2(&pts); pt_clear_entries(&pts, num_contig_lg2); + gather_add_leaf(&unmap->pending, &pts); num_oas += log2_to_int(num_contig_lg2); if (pts.index < flush_start_index) flush_start_index = pts.index; diff --git a/include/linux/generic_pt/common.h b/include/linux/generic_pt/common.h index 2683e5b38998..07ef1c8341a4 100644 --- a/include/linux/generic_pt/common.h +++ b/include/linux/generic_pt/common.h @@ -134,6 +134,11 @@ enum pt_features { * significant amount of page table. */ PT_FEAT_FLUSH_RANGE_NO_GAPS, + /** + * @PT_FEAT_DETAILED_GATHER: Fill in the struct iommu_iotlb_gather pt + * sub structure with information about which levels were changed. + */ + PT_FEAT_DETAILED_GATHER, /* private: */ PT_FEAT_FMT_START, }; diff --git a/include/linux/iommu.h b/include/linux/iommu.h index a9e89911c90f..bf8a77a164e4 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -360,11 +360,31 @@ struct iommu_iotlb_gather { * flushed (inclusive) */ unsigned long end; - /** - * @pgsize: The interval at which to perform the flush, only used - * by arm-smmu-v3 - */ - size_t pgsize; + + union { + /** + * @pgsize: The interval at which to perform the flush, only + * used by arm-smmu-v3 + */ + size_t pgsize; + struct { + /** + * @pt.leaf_levels_bitmap: Bitmap of generic_pt + * levels where leaf entries were unmapped. Bit 0 + * means the leaf only level. If 0 no leafs + * were unmapped. + */ + u8 leaf_levels_bitmap; + /** + * @pt.table_levels_bitmap: Bitmap of generic_pt levels + * of table entries that were removed. Bit 0 is never + * set, bit 1 means a table of all leafs was removed. + * When freelist is empty this must be 0. + */ + u8 table_levels_bitmap; + } pt; + }; + /** * @freelist: Removed pages to free after sync, only used by * iommupt