Skip to content

Commit 3375303

Browse files
Jacob Panjoergroedel
authored andcommitted
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 <[email protected]> Signed-off-by: Jacob Pan <[email protected]> Signed-off-by: Lu Baolu <[email protected]> Reviewed-by: Eric Auger <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Joerg Roedel <[email protected]>
1 parent 24f27d3 commit 3375303

File tree

3 files changed

+93
-0
lines changed

3 files changed

+93
-0
lines changed

drivers/iommu/dmar.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -963,6 +963,7 @@ static int map_iommu(struct intel_iommu *iommu, u64 phys_addr)
963963
warn_invalid_dmar(phys_addr, " returns all ones");
964964
goto unmap;
965965
}
966+
iommu->vccap = dmar_readq(iommu->reg + DMAR_VCCAP_REG);
966967

967968
/* the registers might be more than one page */
968969
map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap),

drivers/iommu/intel-iommu.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1726,6 +1726,9 @@ static void free_dmar_iommu(struct intel_iommu *iommu)
17261726
if (ecap_prs(iommu->ecap))
17271727
intel_svm_finish_prq(iommu);
17281728
}
1729+
if (ecap_vcs(iommu->ecap) && vccap_pasid(iommu->vccap))
1730+
ioasid_unregister_allocator(&iommu->pasid_allocator);
1731+
17291732
#endif
17301733
}
17311734

@@ -3038,6 +3041,85 @@ static int copy_translation_tables(struct intel_iommu *iommu)
30383041
return ret;
30393042
}
30403043

3044+
#ifdef CONFIG_INTEL_IOMMU_SVM
3045+
static ioasid_t intel_vcmd_ioasid_alloc(ioasid_t min, ioasid_t max, void *data)
3046+
{
3047+
struct intel_iommu *iommu = data;
3048+
ioasid_t ioasid;
3049+
3050+
if (!iommu)
3051+
return INVALID_IOASID;
3052+
/*
3053+
* VT-d virtual command interface always uses the full 20 bit
3054+
* PASID range. Host can partition guest PASID range based on
3055+
* policies but it is out of guest's control.
3056+
*/
3057+
if (min < PASID_MIN || max > intel_pasid_max_id)
3058+
return INVALID_IOASID;
3059+
3060+
if (vcmd_alloc_pasid(iommu, &ioasid))
3061+
return INVALID_IOASID;
3062+
3063+
return ioasid;
3064+
}
3065+
3066+
static void intel_vcmd_ioasid_free(ioasid_t ioasid, void *data)
3067+
{
3068+
struct intel_iommu *iommu = data;
3069+
3070+
if (!iommu)
3071+
return;
3072+
/*
3073+
* Sanity check the ioasid owner is done at upper layer, e.g. VFIO
3074+
* We can only free the PASID when all the devices are unbound.
3075+
*/
3076+
if (ioasid_find(NULL, ioasid, NULL)) {
3077+
pr_alert("Cannot free active IOASID %d\n", ioasid);
3078+
return;
3079+
}
3080+
vcmd_free_pasid(iommu, ioasid);
3081+
}
3082+
3083+
static void register_pasid_allocator(struct intel_iommu *iommu)
3084+
{
3085+
/*
3086+
* If we are running in the host, no need for custom allocator
3087+
* in that PASIDs are allocated from the host system-wide.
3088+
*/
3089+
if (!cap_caching_mode(iommu->cap))
3090+
return;
3091+
3092+
if (!sm_supported(iommu)) {
3093+
pr_warn("VT-d Scalable Mode not enabled, no PASID allocation\n");
3094+
return;
3095+
}
3096+
3097+
/*
3098+
* Register a custom PASID allocator if we are running in a guest,
3099+
* guest PASID must be obtained via virtual command interface.
3100+
* There can be multiple vIOMMUs in each guest but only one allocator
3101+
* is active. All vIOMMU allocators will eventually be calling the same
3102+
* host allocator.
3103+
*/
3104+
if (!ecap_vcs(iommu->ecap) || !vccap_pasid(iommu->vccap))
3105+
return;
3106+
3107+
pr_info("Register custom PASID allocator\n");
3108+
iommu->pasid_allocator.alloc = intel_vcmd_ioasid_alloc;
3109+
iommu->pasid_allocator.free = intel_vcmd_ioasid_free;
3110+
iommu->pasid_allocator.pdata = (void *)iommu;
3111+
if (ioasid_register_allocator(&iommu->pasid_allocator)) {
3112+
pr_warn("Custom PASID allocator failed, scalable mode disabled\n");
3113+
/*
3114+
* Disable scalable mode on this IOMMU if there
3115+
* is no custom allocator. Mixing SM capable vIOMMU
3116+
* and non-SM vIOMMU are not supported.
3117+
*/
3118+
intel_iommu_sm = 0;
3119+
}
3120+
}
3121+
#endif
3122+
30413123
static int __init init_dmars(void)
30423124
{
30433125
struct dmar_drhd_unit *drhd;
@@ -3155,6 +3237,9 @@ static int __init init_dmars(void)
31553237
*/
31563238
for_each_active_iommu(iommu, drhd) {
31573239
iommu_flush_write_buffer(iommu);
3240+
#ifdef CONFIG_INTEL_IOMMU_SVM
3241+
register_pasid_allocator(iommu);
3242+
#endif
31583243
iommu_set_root_entry(iommu);
31593244
iommu->flush.flush_context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL);
31603245
iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);

include/linux/intel-iommu.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <linux/iommu.h>
2020
#include <linux/io-64-nonatomic-lo-hi.h>
2121
#include <linux/dmar.h>
22+
#include <linux/ioasid.h>
2223

2324
#include <asm/cacheflush.h>
2425
#include <asm/iommu.h>
@@ -195,6 +196,9 @@
195196
#define ecap_max_handle_mask(e) ((e >> 20) & 0xf)
196197
#define ecap_sc_support(e) ((e >> 7) & 0x1) /* Snooping Control */
197198

199+
/* Virtual command interface capability */
200+
#define vccap_pasid(v) (((v) & DMA_VCS_PAS)) /* PASID allocation */
201+
198202
/* IOTLB_REG */
199203
#define DMA_TLB_FLUSH_GRANU_OFFSET 60
200204
#define DMA_TLB_GLOBAL_FLUSH (((u64)1) << 60)
@@ -288,6 +292,7 @@
288292

289293
/* PRS_REG */
290294
#define DMA_PRS_PPR ((u32)1)
295+
#define DMA_VCS_PAS ((u64)1)
291296

292297
#define IOMMU_WAIT_OP(iommu, offset, op, cond, sts) \
293298
do { \
@@ -555,6 +560,7 @@ struct intel_iommu {
555560
u64 reg_size; /* size of hw register set */
556561
u64 cap;
557562
u64 ecap;
563+
u64 vccap;
558564
u32 gcmd; /* Holds TE, EAFL. Don't need SRTP, SFL, WBF */
559565
raw_spinlock_t register_lock; /* protect register handling */
560566
int seq_id; /* sequence id of the iommu */
@@ -575,6 +581,7 @@ struct intel_iommu {
575581
#ifdef CONFIG_INTEL_IOMMU_SVM
576582
struct page_req_dsc *prq;
577583
unsigned char prq_name[16]; /* Name for PRQ interrupt */
584+
struct ioasid_allocator_ops pasid_allocator; /* Custom allocator for PASIDs */
578585
#endif
579586
struct q_inval *qi; /* Queued invalidation info */
580587
u32 *iommu_state; /* Store iommu states between suspend and resume.*/

0 commit comments

Comments
 (0)