| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * cache.c - Intel VT-d cache invalidation | |
| * | |
| * Copyright (C) 2024 Intel Corporation | |
| * | |
| * Author: Lu Baolu <baolu.lu@linux.intel.com> | |
| */ | |
| #define pr_fmt(fmt) "DMAR: " fmt | |
| #include <linux/dmar.h> | |
| #include <linux/iommu.h> | |
| #include <linux/memory.h> | |
| #include <linux/pci.h> | |
| #include <linux/spinlock.h> | |
| #include "iommu.h" | |
| #include "pasid.h" | |
| #include "trace.h" | |
| /* Check if an existing cache tag can be reused for a new association. */ | |
| static bool cache_tage_match(struct cache_tag *tag, u16 domain_id, | |
| struct intel_iommu *iommu, struct device *dev, | |
| ioasid_t pasid, enum cache_tag_type type) | |
| { | |
| if (tag->type != type) | |
| return false; | |
| if (tag->domain_id != domain_id || tag->pasid != pasid) | |
| return false; | |
| if (type == CACHE_TAG_IOTLB || type == CACHE_TAG_NESTING_IOTLB) | |
| return tag->iommu == iommu; | |
| if (type == CACHE_TAG_DEVTLB || type == CACHE_TAG_NESTING_DEVTLB) | |
| return tag->dev == dev; | |
| return false; | |
| } | |
| /* Assign a cache tag with specified type to domain. */ | |
| int cache_tag_assign(struct dmar_domain *domain, u16 did, struct device *dev, | |
| ioasid_t pasid, enum cache_tag_type type) | |
| { | |
| struct device_domain_info *info = dev_iommu_priv_get(dev); | |
| struct intel_iommu *iommu = info->iommu; | |
| struct cache_tag *tag, *temp; | |
| struct list_head *prev; | |
| unsigned long flags; | |
| tag = kzalloc_obj(*tag); | |
| if (!tag) | |
| return -ENOMEM; | |
| tag->type = type; | |
| tag->iommu = iommu; | |
| tag->domain_id = did; | |
| tag->pasid = pasid; | |
| tag->users = 1; | |
| if (type == CACHE_TAG_DEVTLB || type == CACHE_TAG_NESTING_DEVTLB) | |
| tag->dev = dev; | |
| else | |
| tag->dev = iommu->iommu.dev; | |
| spin_lock_irqsave(&domain->cache_lock, flags); | |
| prev = &domain->cache_tags; | |
| list_for_each_entry(temp, &domain->cache_tags, node) { | |
| if (cache_tage_match(temp, did, iommu, dev, pasid, type)) { | |
| temp->users++; | |
| spin_unlock_irqrestore(&domain->cache_lock, flags); | |
| kfree(tag); | |
| trace_cache_tag_assign(temp); | |
| return 0; | |
| } | |
| if (temp->iommu == iommu) | |
| prev = &temp->node; | |
| } | |
| /* | |
| * Link cache tags of same iommu unit together, so corresponding | |
| * flush ops can be batched for iommu unit. | |
| */ | |
| list_add(&tag->node, prev); | |
| spin_unlock_irqrestore(&domain->cache_lock, flags); | |
| trace_cache_tag_assign(tag); | |
| return 0; | |
| } | |
| /* Unassign a cache tag with specified type from domain. */ | |
| static void cache_tag_unassign(struct dmar_domain *domain, u16 did, | |
| struct device *dev, ioasid_t pasid, | |
| enum cache_tag_type type) | |
| { | |
| struct device_domain_info *info = dev_iommu_priv_get(dev); | |
| struct intel_iommu *iommu = info->iommu; | |
| struct cache_tag *tag; | |
| unsigned long flags; | |
| spin_lock_irqsave(&domain->cache_lock, flags); | |
| list_for_each_entry(tag, &domain->cache_tags, node) { | |
| if (cache_tage_match(tag, did, iommu, dev, pasid, type)) { | |
| trace_cache_tag_unassign(tag); | |
| if (--tag->users == 0) { | |
| list_del(&tag->node); | |
| kfree(tag); | |
| } | |
| break; | |
| } | |
| } | |
| spin_unlock_irqrestore(&domain->cache_lock, flags); | |
| } | |
| /* domain->qi_batch will be freed in iommu_free_domain() path. */ | |
| static int domain_qi_batch_alloc(struct dmar_domain *domain) | |
| { | |
| unsigned long flags; | |
| int ret = 0; | |
| spin_lock_irqsave(&domain->cache_lock, flags); | |
| if (domain->qi_batch) | |
| goto out_unlock; | |
| domain->qi_batch = kzalloc_obj(*domain->qi_batch, GFP_ATOMIC); | |
| if (!domain->qi_batch) | |
| ret = -ENOMEM; | |
| out_unlock: | |
| spin_unlock_irqrestore(&domain->cache_lock, flags); | |
| return ret; | |
| } | |
| static int __cache_tag_assign_domain(struct dmar_domain *domain, u16 did, | |
| struct device *dev, ioasid_t pasid) | |
| { | |
| struct device_domain_info *info = dev_iommu_priv_get(dev); | |
| int ret; | |
| ret = domain_qi_batch_alloc(domain); | |
| if (ret) | |
| return ret; | |
| ret = cache_tag_assign(domain, did, dev, pasid, CACHE_TAG_IOTLB); | |
| if (ret || !info->ats_enabled) | |
| return ret; | |
| ret = cache_tag_assign(domain, did, dev, pasid, CACHE_TAG_DEVTLB); | |
| if (ret) | |
| cache_tag_unassign(domain, did, dev, pasid, CACHE_TAG_IOTLB); | |
| return ret; | |
| } | |
| static void __cache_tag_unassign_domain(struct dmar_domain *domain, u16 did, | |
| struct device *dev, ioasid_t pasid) | |
| { | |
| struct device_domain_info *info = dev_iommu_priv_get(dev); | |
| cache_tag_unassign(domain, did, dev, pasid, CACHE_TAG_IOTLB); | |
| if (info->ats_enabled) | |
| cache_tag_unassign(domain, did, dev, pasid, CACHE_TAG_DEVTLB); | |
| } | |
| static int __cache_tag_assign_parent_domain(struct dmar_domain *domain, u16 did, | |
| struct device *dev, ioasid_t pasid) | |
| { | |
| struct device_domain_info *info = dev_iommu_priv_get(dev); | |
| int ret; | |
| ret = domain_qi_batch_alloc(domain); | |
| if (ret) | |
| return ret; | |
| ret = cache_tag_assign(domain, did, dev, pasid, CACHE_TAG_NESTING_IOTLB); | |
| if (ret || !info->ats_enabled) | |
| return ret; | |
| ret = cache_tag_assign(domain, did, dev, pasid, CACHE_TAG_NESTING_DEVTLB); | |
| if (ret) | |
| cache_tag_unassign(domain, did, dev, pasid, CACHE_TAG_NESTING_IOTLB); | |
| return ret; | |
| } | |
| static void __cache_tag_unassign_parent_domain(struct dmar_domain *domain, u16 did, | |
| struct device *dev, ioasid_t pasid) | |
| { | |
| struct device_domain_info *info = dev_iommu_priv_get(dev); | |
| cache_tag_unassign(domain, did, dev, pasid, CACHE_TAG_NESTING_IOTLB); | |
| if (info->ats_enabled) | |
| cache_tag_unassign(domain, did, dev, pasid, CACHE_TAG_NESTING_DEVTLB); | |
| } | |
| static u16 domain_get_id_for_dev(struct dmar_domain *domain, struct device *dev) | |
| { | |
| struct device_domain_info *info = dev_iommu_priv_get(dev); | |
| struct intel_iommu *iommu = info->iommu; | |
| /* | |
| * The driver assigns different domain IDs for all domains except | |
| * the SVA type. | |
| */ | |
| if (domain->domain.type == IOMMU_DOMAIN_SVA) | |
| return FLPT_DEFAULT_DID; | |
| return domain_id_iommu(domain, iommu); | |
| } | |
| /* | |
| * Assign cache tags to a domain when it's associated with a device's | |
| * PASID using a specific domain ID. | |
| * | |
| * On success (return value of 0), cache tags are created and added to the | |
| * domain's cache tag list. On failure (negative return value), an error | |
| * code is returned indicating the reason for the failure. | |
| */ | |
| int cache_tag_assign_domain(struct dmar_domain *domain, | |
| struct device *dev, ioasid_t pasid) | |
| { | |
| u16 did = domain_get_id_for_dev(domain, dev); | |
| int ret; | |
| ret = __cache_tag_assign_domain(domain, did, dev, pasid); | |
| if (ret || domain->domain.type != IOMMU_DOMAIN_NESTED) | |
| return ret; | |
| ret = __cache_tag_assign_parent_domain(domain->s2_domain, did, dev, pasid); | |
| if (ret) | |
| __cache_tag_unassign_domain(domain, did, dev, pasid); | |
| return ret; | |
| } | |
| /* | |
| * Remove the cache tags associated with a device's PASID when the domain is | |
| * detached from the device. | |
| * | |
| * The cache tags must be previously assigned to the domain by calling the | |
| * assign interface. | |
| */ | |
| void cache_tag_unassign_domain(struct dmar_domain *domain, | |
| struct device *dev, ioasid_t pasid) | |
| { | |
| u16 did = domain_get_id_for_dev(domain, dev); | |
| __cache_tag_unassign_domain(domain, did, dev, pasid); | |
| if (domain->domain.type == IOMMU_DOMAIN_NESTED) | |
| __cache_tag_unassign_parent_domain(domain->s2_domain, did, dev, pasid); | |
| } | |
| static unsigned long calculate_psi_aligned_address(unsigned long start, | |
| unsigned long last, | |
| unsigned long *size_order) | |
| { | |
| unsigned int sz_lg2; | |
| /* Compute a sz_lg2 that spans start and last */ | |
| start &= GENMASK(BITS_PER_LONG - 1, VTD_PAGE_SHIFT); | |
| sz_lg2 = fls_long(start ^ last); | |
| if (sz_lg2 <= 12) { | |
| *size_order = 0; | |
| return start; | |
| } | |
| if (unlikely(sz_lg2 >= BITS_PER_LONG)) { | |
| /* | |
| * MAX_AGAW_PFN_WIDTH triggers full invalidation in all | |
| * downstream users. | |
| */ | |
| *size_order = MAX_AGAW_PFN_WIDTH; | |
| return 0; | |
| } | |
| *size_order = sz_lg2 - VTD_PAGE_SHIFT; | |
| return start & GENMASK(BITS_PER_LONG - 1, sz_lg2); | |
| } | |
| static void qi_batch_flush_descs(struct intel_iommu *iommu, struct qi_batch *batch) | |
| { | |
| if (!iommu || !batch->index) | |
| return; | |
| qi_submit_sync(iommu, batch->descs, batch->index, 0); | |
| /* Reset the index value and clean the whole batch buffer. */ | |
| memset(batch, 0, sizeof(*batch)); | |
| } | |
| static void qi_batch_increment_index(struct intel_iommu *iommu, struct qi_batch *batch) | |
| { | |
| if (++batch->index == QI_MAX_BATCHED_DESC_COUNT) | |
| qi_batch_flush_descs(iommu, batch); | |
| } | |
| static void qi_batch_add_iotlb(struct intel_iommu *iommu, u16 did, u64 addr, | |
| unsigned int size_order, u64 type, | |
| struct qi_batch *batch) | |
| { | |
| qi_desc_iotlb(iommu, did, addr, size_order, type, &batch->descs[batch->index]); | |
| qi_batch_increment_index(iommu, batch); | |
| } | |
| static void qi_batch_add_dev_iotlb(struct intel_iommu *iommu, u16 sid, u16 pfsid, | |
| u16 qdep, u64 addr, unsigned int mask, | |
| struct qi_batch *batch) | |
| { | |
| /* | |
| * According to VT-d spec, software is recommended to not submit any Device-TLB | |
| * invalidation requests while address remapping hardware is disabled. | |
| */ | |
| if (!(iommu->gcmd & DMA_GCMD_TE)) | |
| return; | |
| qi_desc_dev_iotlb(sid, pfsid, qdep, addr, mask, &batch->descs[batch->index]); | |
| qi_batch_increment_index(iommu, batch); | |
| } | |
| static void qi_batch_add_piotlb_all(struct intel_iommu *iommu, u16 did, | |
| u32 pasid, struct qi_batch *batch) | |
| { | |
| qi_desc_piotlb_all(did, pasid, &batch->descs[batch->index]); | |
| qi_batch_increment_index(iommu, batch); | |
| } | |
| static void qi_batch_add_piotlb(struct intel_iommu *iommu, u16 did, u32 pasid, | |
| u64 addr, unsigned int size_order, bool ih, | |
| struct qi_batch *batch) | |
| { | |
| qi_desc_piotlb(did, pasid, addr, size_order, ih, | |
| &batch->descs[batch->index]); | |
| qi_batch_increment_index(iommu, batch); | |
| } | |
| static void qi_batch_add_pasid_dev_iotlb(struct intel_iommu *iommu, u16 sid, u16 pfsid, | |
| u32 pasid, u16 qdep, u64 addr, | |
| unsigned int size_order, struct qi_batch *batch) | |
| { | |
| /* | |
| * According to VT-d spec, software is recommended to not submit any | |
| * Device-TLB invalidation requests while address remapping hardware | |
| * is disabled. | |
| */ | |
| if (!(iommu->gcmd & DMA_GCMD_TE)) | |
| return; | |
| qi_desc_dev_iotlb_pasid(sid, pfsid, pasid, qdep, addr, size_order, | |
| &batch->descs[batch->index]); | |
| qi_batch_increment_index(iommu, batch); | |
| } | |
| static bool intel_domain_use_piotlb(struct dmar_domain *domain) | |
| { | |
| return domain->domain.type == IOMMU_DOMAIN_SVA || | |
| domain->domain.type == IOMMU_DOMAIN_NESTED || | |
| intel_domain_is_fs_paging(domain); | |
| } | |
| static void cache_tag_flush_iotlb(struct dmar_domain *domain, struct cache_tag *tag, | |
| unsigned long addr, unsigned long mask, int ih) | |
| { | |
| struct intel_iommu *iommu = tag->iommu; | |
| u64 type = DMA_TLB_PSI_FLUSH; | |
| if (intel_domain_use_piotlb(domain)) { | |
| if (mask >= MAX_AGAW_PFN_WIDTH) | |
| qi_batch_add_piotlb_all(iommu, tag->domain_id, | |
| tag->pasid, domain->qi_batch); | |
| else | |
| qi_batch_add_piotlb(iommu, tag->domain_id, tag->pasid, | |
| addr, mask, ih, domain->qi_batch); | |
| return; | |
| } | |
| /* | |
| * Fallback to domain selective flush if no PSI support or the size | |
| * is too big. | |
| */ | |
| if (!cap_pgsel_inv(iommu->cap) || | |
| mask > cap_max_amask_val(iommu->cap)) { | |
| addr = 0; | |
| mask = 0; | |
| ih = 0; | |
| type = DMA_TLB_DSI_FLUSH; | |
| } | |
| if (ecap_qis(iommu->ecap)) | |
| qi_batch_add_iotlb(iommu, tag->domain_id, addr | ih, mask, type, | |
| domain->qi_batch); | |
| else | |
| __iommu_flush_iotlb(iommu, tag->domain_id, addr | ih, mask, type); | |
| } | |
| static void cache_tag_flush_devtlb_psi(struct dmar_domain *domain, struct cache_tag *tag, | |
| unsigned long addr, unsigned long mask) | |
| { | |
| struct intel_iommu *iommu = tag->iommu; | |
| struct device_domain_info *info; | |
| u16 sid; | |
| info = dev_iommu_priv_get(tag->dev); | |
| sid = PCI_DEVID(info->bus, info->devfn); | |
| if (tag->pasid == IOMMU_NO_PASID) { | |
| qi_batch_add_dev_iotlb(iommu, sid, info->pfsid, info->ats_qdep, | |
| addr, mask, domain->qi_batch); | |
| if (info->dtlb_extra_inval) | |
| qi_batch_add_dev_iotlb(iommu, sid, info->pfsid, info->ats_qdep, | |
| addr, mask, domain->qi_batch); | |
| return; | |
| } | |
| qi_batch_add_pasid_dev_iotlb(iommu, sid, info->pfsid, tag->pasid, | |
| info->ats_qdep, addr, mask, domain->qi_batch); | |
| if (info->dtlb_extra_inval) | |
| qi_batch_add_pasid_dev_iotlb(iommu, sid, info->pfsid, tag->pasid, | |
| info->ats_qdep, addr, mask, | |
| domain->qi_batch); | |
| } | |
| /* | |
| * Invalidates a range of IOVA from @start (inclusive) to @end (inclusive) | |
| * when the memory mappings in the target domain have been modified. | |
| */ | |
| void cache_tag_flush_range(struct dmar_domain *domain, unsigned long start, | |
| unsigned long end, int ih) | |
| { | |
| struct intel_iommu *iommu = NULL; | |
| unsigned long mask, addr; | |
| struct cache_tag *tag; | |
| unsigned long flags; | |
| addr = calculate_psi_aligned_address(start, end, &mask); | |
| spin_lock_irqsave(&domain->cache_lock, flags); | |
| list_for_each_entry(tag, &domain->cache_tags, node) { | |
| if (iommu && iommu != tag->iommu) | |
| qi_batch_flush_descs(iommu, domain->qi_batch); | |
| iommu = tag->iommu; | |
| switch (tag->type) { | |
| case CACHE_TAG_IOTLB: | |
| case CACHE_TAG_NESTING_IOTLB: | |
| cache_tag_flush_iotlb(domain, tag, addr, mask, ih); | |
| break; | |
| case CACHE_TAG_NESTING_DEVTLB: | |
| /* | |
| * Address translation cache in device side caches the | |
| * result of nested translation. There is no easy way | |
| * to identify the exact set of nested translations | |
| * affected by a change in S2. So just flush the entire | |
| * device cache. | |
| */ | |
| addr = 0; | |
| mask = MAX_AGAW_PFN_WIDTH; | |
| fallthrough; | |
| case CACHE_TAG_DEVTLB: | |
| cache_tag_flush_devtlb_psi(domain, tag, addr, mask); | |
| break; | |
| } | |
| trace_cache_tag_flush_range(tag, start, end, addr, mask); | |
| } | |
| qi_batch_flush_descs(iommu, domain->qi_batch); | |
| spin_unlock_irqrestore(&domain->cache_lock, flags); | |
| } | |
| /* | |
| * Invalidates all ranges of IOVA when the memory mappings in the target | |
| * domain have been modified. | |
| */ | |
| void cache_tag_flush_all(struct dmar_domain *domain) | |
| { | |
| cache_tag_flush_range(domain, 0, ULONG_MAX, 0); | |
| } | |
| /* | |
| * Invalidate a range of IOVA when new mappings are created in the target | |
| * domain. | |
| * | |
| * - VT-d spec, Section 6.1 Caching Mode: When the CM field is reported as | |
| * Set, any software updates to remapping structures other than first- | |
| * stage mapping requires explicit invalidation of the caches. | |
| * - VT-d spec, Section 6.8 Write Buffer Flushing: For hardware that requires | |
| * write buffer flushing, software must explicitly perform write-buffer | |
| * flushing, if cache invalidation is not required. | |
| */ | |
| void cache_tag_flush_range_np(struct dmar_domain *domain, unsigned long start, | |
| unsigned long end) | |
| { | |
| struct intel_iommu *iommu = NULL; | |
| unsigned long mask, addr; | |
| struct cache_tag *tag; | |
| unsigned long flags; | |
| addr = calculate_psi_aligned_address(start, end, &mask); | |
| spin_lock_irqsave(&domain->cache_lock, flags); | |
| list_for_each_entry(tag, &domain->cache_tags, node) { | |
| if (iommu && iommu != tag->iommu) | |
| qi_batch_flush_descs(iommu, domain->qi_batch); | |
| iommu = tag->iommu; | |
| if (!cap_caching_mode(iommu->cap) || | |
| intel_domain_is_fs_paging(domain)) { | |
| iommu_flush_write_buffer(iommu); | |
| continue; | |
| } | |
| if (tag->type == CACHE_TAG_IOTLB || | |
| tag->type == CACHE_TAG_NESTING_IOTLB) | |
| cache_tag_flush_iotlb(domain, tag, addr, mask, 0); | |
| trace_cache_tag_flush_range_np(tag, start, end, addr, mask); | |
| } | |
| qi_batch_flush_descs(iommu, domain->qi_batch); | |
| spin_unlock_irqrestore(&domain->cache_lock, flags); | |
| } |