Re: [Patch Part3 V5 7/8] iommu/vt-d: Enhance intel-iommu driver to support DMAR unit hotplug

From: Yijing Wang
Date: Sun Sep 14 2014 - 22:54:59 EST


On 2014/9/12 10:10, Jiang Liu wrote:
> Implement required callback functions for intel-iommu driver
> to support DMAR unit hotplug.
>

Reviewed-by: Yijing Wang <wangyijing@xxxxxxxxxx>

> Signed-off-by: Jiang Liu <jiang.liu@xxxxxxxxxxxxxxx>
> ---
> drivers/iommu/intel-iommu.c | 206 +++++++++++++++++++++++++++++++------------
> 1 file changed, 151 insertions(+), 55 deletions(-)
>
> diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
> index 70d9d47eaeda..c2d369524960 100644
> --- a/drivers/iommu/intel-iommu.c
> +++ b/drivers/iommu/intel-iommu.c
> @@ -1125,8 +1125,11 @@ static int iommu_alloc_root_entry(struct intel_iommu *iommu)
> unsigned long flags;
>
> root = (struct root_entry *)alloc_pgtable_page(iommu->node);
> - if (!root)
> + if (!root) {
> + pr_err("IOMMU: allocating root entry for %s failed\n",
> + iommu->name);
> return -ENOMEM;
> + }
>
> __iommu_flush_cache(iommu, root, ROOT_SIZE);
>
> @@ -1466,7 +1469,7 @@ static int iommu_init_domains(struct intel_iommu *iommu)
> return 0;
> }
>
> -static void free_dmar_iommu(struct intel_iommu *iommu)
> +static void disable_dmar_iommu(struct intel_iommu *iommu)
> {
> struct dmar_domain *domain;
> int i;
> @@ -1490,11 +1493,16 @@ static void free_dmar_iommu(struct intel_iommu *iommu)
>
> if (iommu->gcmd & DMA_GCMD_TE)
> iommu_disable_translation(iommu);
> +}
>
> - kfree(iommu->domains);
> - kfree(iommu->domain_ids);
> - iommu->domains = NULL;
> - iommu->domain_ids = NULL;
> +static void free_dmar_iommu(struct intel_iommu *iommu)
> +{
> + if ((iommu->domains) && (iommu->domain_ids)) {
> + kfree(iommu->domains);
> + kfree(iommu->domain_ids);
> + iommu->domains = NULL;
> + iommu->domain_ids = NULL;
> + }
>
> g_iommus[iommu->seq_id] = NULL;
>
> @@ -2701,6 +2709,41 @@ static int __init iommu_prepare_static_identity_mapping(int hw)
> return 0;
> }
>
> +static void intel_iommu_init_qi(struct intel_iommu *iommu)
> +{
> + /*
> + * Start from the sane iommu hardware state.
> + * If the queued invalidation is already initialized by us
> + * (for example, while enabling interrupt-remapping) then
> + * we got the things already rolling from a sane state.
> + */
> + if (!iommu->qi) {
> + /*
> + * Clear any previous faults.
> + */
> + dmar_fault(-1, iommu);
> + /*
> + * Disable queued invalidation if supported and already enabled
> + * before OS handover.
> + */
> + dmar_disable_qi(iommu);
> + }
> +
> + if (dmar_enable_qi(iommu)) {
> + /*
> + * Queued Invalidate not enabled, use Register Based Invalidate
> + */
> + iommu->flush.flush_context = __iommu_flush_context;
> + iommu->flush.flush_iotlb = __iommu_flush_iotlb;
> + pr_info("IOMMU: %s using Register based invalidation\n",
> + iommu->name);
> + } else {
> + iommu->flush.flush_context = qi_flush_context;
> + iommu->flush.flush_iotlb = qi_flush_iotlb;
> + pr_info("IOMMU: %s using Queued invalidation\n", iommu->name);
> + }
> +}
> +
> static int __init init_dmars(void)
> {
> struct dmar_drhd_unit *drhd;
> @@ -2729,6 +2772,10 @@ static int __init init_dmars(void)
> DMAR_UNITS_SUPPORTED);
> }
>
> + /* Preallocate enough resources for IOMMU hot-addition */
> + if (g_num_of_iommus < DMAR_UNITS_SUPPORTED)
> + g_num_of_iommus = DMAR_UNITS_SUPPORTED;
> +
> g_iommus = kcalloc(g_num_of_iommus, sizeof(struct intel_iommu *),
> GFP_KERNEL);
> if (!g_iommus) {
> @@ -2757,58 +2804,14 @@ static int __init init_dmars(void)
> * among all IOMMU's. Need to Split it later.
> */
> ret = iommu_alloc_root_entry(iommu);
> - if (ret) {
> - printk(KERN_ERR "IOMMU: allocate root entry failed\n");
> + if (ret)
> goto free_iommu;
> - }
> if (!ecap_pass_through(iommu->ecap))
> hw_pass_through = 0;
> }
>
> - /*
> - * Start from the sane iommu hardware state.
> - */
> - for_each_active_iommu(iommu, drhd) {
> - /*
> - * If the queued invalidation is already initialized by us
> - * (for example, while enabling interrupt-remapping) then
> - * we got the things already rolling from a sane state.
> - */
> - if (iommu->qi)
> - continue;
> -
> - /*
> - * Clear any previous faults.
> - */
> - dmar_fault(-1, iommu);
> - /*
> - * Disable queued invalidation if supported and already enabled
> - * before OS handover.
> - */
> - dmar_disable_qi(iommu);
> - }
> -
> - for_each_active_iommu(iommu, drhd) {
> - if (dmar_enable_qi(iommu)) {
> - /*
> - * Queued Invalidate not enabled, use Register Based
> - * Invalidate
> - */
> - iommu->flush.flush_context = __iommu_flush_context;
> - iommu->flush.flush_iotlb = __iommu_flush_iotlb;
> - printk(KERN_INFO "IOMMU %d 0x%Lx: using Register based "
> - "invalidation\n",
> - iommu->seq_id,
> - (unsigned long long)drhd->reg_base_addr);
> - } else {
> - iommu->flush.flush_context = qi_flush_context;
> - iommu->flush.flush_iotlb = qi_flush_iotlb;
> - printk(KERN_INFO "IOMMU %d 0x%Lx: using Queued "
> - "invalidation\n",
> - iommu->seq_id,
> - (unsigned long long)drhd->reg_base_addr);
> - }
> - }
> + for_each_active_iommu(iommu, drhd)
> + intel_iommu_init_qi(iommu);
>
> if (iommu_pass_through)
> iommu_identity_mapping |= IDENTMAP_ALL;
> @@ -2894,8 +2897,10 @@ static int __init init_dmars(void)
> return 0;
>
> free_iommu:
> - for_each_active_iommu(iommu, drhd)
> + for_each_active_iommu(iommu, drhd) {
> + disable_dmar_iommu(iommu);
> free_dmar_iommu(iommu);
> + }
> kfree(deferred_flush);
> free_g_iommus:
> kfree(g_iommus);
> @@ -3801,9 +3806,100 @@ int dmar_check_one_atsr(struct acpi_dmar_header *hdr, void *arg)
> return 0;
> }
>
> +static int intel_iommu_add(struct dmar_drhd_unit *dmaru)
> +{
> + int sp, ret = 0;
> + struct intel_iommu *iommu = dmaru->iommu;
> +
> + if (g_iommus[iommu->seq_id])
> + return 0;
> +
> + if (hw_pass_through && !ecap_pass_through(iommu->ecap)) {
> + pr_warn("IOMMU: %s doesn't support hardware pass through.\n",
> + iommu->name);
> + return -ENXIO;
> + }
> + if (!ecap_sc_support(iommu->ecap) &&
> + domain_update_iommu_snooping(iommu)) {
> + pr_warn("IOMMU: %s doesn't support snooping.\n",
> + iommu->name);
> + return -ENXIO;
> + }
> + sp = domain_update_iommu_superpage(iommu) - 1;
> + if (sp >= 0 && !(cap_super_page_val(iommu->cap) & (1 << sp))) {
> + pr_warn("IOMMU: %s doesn't support large page.\n",
> + iommu->name);
> + return -ENXIO;
> + }
> +
> + /*
> + * Disable translation if already enabled prior to OS handover.
> + */
> + if (iommu->gcmd & DMA_GCMD_TE)
> + iommu_disable_translation(iommu);
> +
> + g_iommus[iommu->seq_id] = iommu;
> + ret = iommu_init_domains(iommu);
> + if (ret == 0)
> + ret = iommu_alloc_root_entry(iommu);
> + if (ret)
> + goto out;
> +
> + if (dmaru->ignored) {
> + /*
> + * we always have to disable PMRs or DMA may fail on this device
> + */
> + if (force_on)
> + iommu_disable_protect_mem_regions(iommu);
> + return 0;
> + }
> +
> + intel_iommu_init_qi(iommu);
> + iommu_flush_write_buffer(iommu);
> + ret = dmar_set_interrupt(iommu);
> + if (ret)
> + goto disable_iommu;
> +
> + 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);
> + iommu_enable_translation(iommu);
> +
> + if (si_domain) {
> + ret = iommu_attach_domain(si_domain, iommu);
> + if (ret < 0 || si_domain->id != ret)
> + goto disable_iommu;
> + domain_attach_iommu(si_domain, iommu);
> + }
> +
> + iommu_disable_protect_mem_regions(iommu);
> + return 0;
> +
> +disable_iommu:
> + disable_dmar_iommu(iommu);
> +out:
> + free_dmar_iommu(iommu);
> + return ret;
> +}
> +
> int dmar_iommu_hotplug(struct dmar_drhd_unit *dmaru, bool insert)
> {
> - return intel_iommu_enabled ? -ENOSYS : 0;
> + int ret = 0;
> + struct intel_iommu *iommu = dmaru->iommu;
> +
> + if (!intel_iommu_enabled)
> + return 0;
> + if (iommu == NULL)
> + return -EINVAL;
> +
> + if (insert) {
> + ret = intel_iommu_add(dmaru);
> + } else {
> + disable_dmar_iommu(iommu);
> + free_dmar_iommu(iommu);
> + }
> +
> + return ret;
> }
>
> static void intel_iommu_free_dmars(void)
>


--
Thanks!
Yijing

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/