mirror of
https://github.com/edk2-porting/linux-next.git
synced 2025-01-08 21:53:54 +08:00
iommu/vt-d: Add custom allocator for IOASID
When VT-d driver runs in the guest, PASID allocation must be performed via virtual command interface. This patch registers a custom IOASID allocator which takes precedence over the default XArray based allocator. The resulting IOASID allocation will always come from the host. This ensures that PASID namespace is system- wide. Virtual command registers are used in the guest only, to prevent vmexit cost, we cache the capability and store it during initialization. Signed-off-by: Liu, Yi L <yi.l.liu@intel.com> Signed-off-by: Jacob Pan <jacob.jun.pan@linux.intel.com> Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Link: https://lore.kernel.org/r/20200516062101.29541-9-baolu.lu@linux.intel.com Signed-off-by: Joerg Roedel <jroedel@suse.de>
This commit is contained in:
parent
24f27d32ab
commit
3375303e82
@ -963,6 +963,7 @@ static int map_iommu(struct intel_iommu *iommu, u64 phys_addr)
|
||||
warn_invalid_dmar(phys_addr, " returns all ones");
|
||||
goto unmap;
|
||||
}
|
||||
iommu->vccap = dmar_readq(iommu->reg + DMAR_VCCAP_REG);
|
||||
|
||||
/* the registers might be more than one page */
|
||||
map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap),
|
||||
|
@ -1726,6 +1726,9 @@ static void free_dmar_iommu(struct intel_iommu *iommu)
|
||||
if (ecap_prs(iommu->ecap))
|
||||
intel_svm_finish_prq(iommu);
|
||||
}
|
||||
if (ecap_vcs(iommu->ecap) && vccap_pasid(iommu->vccap))
|
||||
ioasid_unregister_allocator(&iommu->pasid_allocator);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -3038,6 +3041,85 @@ out_unmap:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_INTEL_IOMMU_SVM
|
||||
static ioasid_t intel_vcmd_ioasid_alloc(ioasid_t min, ioasid_t max, void *data)
|
||||
{
|
||||
struct intel_iommu *iommu = data;
|
||||
ioasid_t ioasid;
|
||||
|
||||
if (!iommu)
|
||||
return INVALID_IOASID;
|
||||
/*
|
||||
* VT-d virtual command interface always uses the full 20 bit
|
||||
* PASID range. Host can partition guest PASID range based on
|
||||
* policies but it is out of guest's control.
|
||||
*/
|
||||
if (min < PASID_MIN || max > intel_pasid_max_id)
|
||||
return INVALID_IOASID;
|
||||
|
||||
if (vcmd_alloc_pasid(iommu, &ioasid))
|
||||
return INVALID_IOASID;
|
||||
|
||||
return ioasid;
|
||||
}
|
||||
|
||||
static void intel_vcmd_ioasid_free(ioasid_t ioasid, void *data)
|
||||
{
|
||||
struct intel_iommu *iommu = data;
|
||||
|
||||
if (!iommu)
|
||||
return;
|
||||
/*
|
||||
* Sanity check the ioasid owner is done at upper layer, e.g. VFIO
|
||||
* We can only free the PASID when all the devices are unbound.
|
||||
*/
|
||||
if (ioasid_find(NULL, ioasid, NULL)) {
|
||||
pr_alert("Cannot free active IOASID %d\n", ioasid);
|
||||
return;
|
||||
}
|
||||
vcmd_free_pasid(iommu, ioasid);
|
||||
}
|
||||
|
||||
static void register_pasid_allocator(struct intel_iommu *iommu)
|
||||
{
|
||||
/*
|
||||
* If we are running in the host, no need for custom allocator
|
||||
* in that PASIDs are allocated from the host system-wide.
|
||||
*/
|
||||
if (!cap_caching_mode(iommu->cap))
|
||||
return;
|
||||
|
||||
if (!sm_supported(iommu)) {
|
||||
pr_warn("VT-d Scalable Mode not enabled, no PASID allocation\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register a custom PASID allocator if we are running in a guest,
|
||||
* guest PASID must be obtained via virtual command interface.
|
||||
* There can be multiple vIOMMUs in each guest but only one allocator
|
||||
* is active. All vIOMMU allocators will eventually be calling the same
|
||||
* host allocator.
|
||||
*/
|
||||
if (!ecap_vcs(iommu->ecap) || !vccap_pasid(iommu->vccap))
|
||||
return;
|
||||
|
||||
pr_info("Register custom PASID allocator\n");
|
||||
iommu->pasid_allocator.alloc = intel_vcmd_ioasid_alloc;
|
||||
iommu->pasid_allocator.free = intel_vcmd_ioasid_free;
|
||||
iommu->pasid_allocator.pdata = (void *)iommu;
|
||||
if (ioasid_register_allocator(&iommu->pasid_allocator)) {
|
||||
pr_warn("Custom PASID allocator failed, scalable mode disabled\n");
|
||||
/*
|
||||
* Disable scalable mode on this IOMMU if there
|
||||
* is no custom allocator. Mixing SM capable vIOMMU
|
||||
* and non-SM vIOMMU are not supported.
|
||||
*/
|
||||
intel_iommu_sm = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __init init_dmars(void)
|
||||
{
|
||||
struct dmar_drhd_unit *drhd;
|
||||
@ -3155,6 +3237,9 @@ static int __init init_dmars(void)
|
||||
*/
|
||||
for_each_active_iommu(iommu, drhd) {
|
||||
iommu_flush_write_buffer(iommu);
|
||||
#ifdef CONFIG_INTEL_IOMMU_SVM
|
||||
register_pasid_allocator(iommu);
|
||||
#endif
|
||||
iommu_set_root_entry(iommu);
|
||||
iommu->flush.flush_context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL);
|
||||
iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
#include <linux/dmar.h>
|
||||
#include <linux/ioasid.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/iommu.h>
|
||||
@ -195,6 +196,9 @@
|
||||
#define ecap_max_handle_mask(e) ((e >> 20) & 0xf)
|
||||
#define ecap_sc_support(e) ((e >> 7) & 0x1) /* Snooping Control */
|
||||
|
||||
/* Virtual command interface capability */
|
||||
#define vccap_pasid(v) (((v) & DMA_VCS_PAS)) /* PASID allocation */
|
||||
|
||||
/* IOTLB_REG */
|
||||
#define DMA_TLB_FLUSH_GRANU_OFFSET 60
|
||||
#define DMA_TLB_GLOBAL_FLUSH (((u64)1) << 60)
|
||||
@ -288,6 +292,7 @@
|
||||
|
||||
/* PRS_REG */
|
||||
#define DMA_PRS_PPR ((u32)1)
|
||||
#define DMA_VCS_PAS ((u64)1)
|
||||
|
||||
#define IOMMU_WAIT_OP(iommu, offset, op, cond, sts) \
|
||||
do { \
|
||||
@ -555,6 +560,7 @@ struct intel_iommu {
|
||||
u64 reg_size; /* size of hw register set */
|
||||
u64 cap;
|
||||
u64 ecap;
|
||||
u64 vccap;
|
||||
u32 gcmd; /* Holds TE, EAFL. Don't need SRTP, SFL, WBF */
|
||||
raw_spinlock_t register_lock; /* protect register handling */
|
||||
int seq_id; /* sequence id of the iommu */
|
||||
@ -575,6 +581,7 @@ struct intel_iommu {
|
||||
#ifdef CONFIG_INTEL_IOMMU_SVM
|
||||
struct page_req_dsc *prq;
|
||||
unsigned char prq_name[16]; /* Name for PRQ interrupt */
|
||||
struct ioasid_allocator_ops pasid_allocator; /* Custom allocator for PASIDs */
|
||||
#endif
|
||||
struct q_inval *qi; /* Queued invalidation info */
|
||||
u32 *iommu_state; /* Store iommu states between suspend and resume.*/
|
||||
|
Loading…
Reference in New Issue
Block a user