Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

iommu/vt-d: Fix possible recursive locking in intel_iommu_init()

The global rwsem dmar_global_lock was introduced by commit 3a5670e8ac932
("iommu/vt-d: Introduce a rwsem to protect global data structures"). It
is used to protect DMAR related global data from DMAR hotplug operations.

The dmar_global_lock used in the intel_iommu_init() might cause recursive
locking issue, for example, intel_iommu_get_resv_regions() is taking the
dmar_global_lock from within a section where intel_iommu_init() already
holds it via probe_acpi_namespace_devices().

Using dmar_global_lock in intel_iommu_init() could be relaxed since it is
unlikely that any IO board must be hot added before the IOMMU subsystem is
initialized. This eliminates the possible recursive locking issue by moving
down DMAR hotplug support after the IOMMU is initialized and removing the
uses of dmar_global_lock in intel_iommu_init().

Fixes: d5692d4af08cd ("iommu/vt-d: Fix suspicious RCU usage in probe_acpi_namespace_devices()")
Reported-by: Robin Murphy <robin.murphy@arm.com>
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Link: https://lore.kernel.org/r/894db0ccae854b35c73814485569b634237b5538.1657034828.git.robin.murphy@arm.com
Link: https://lore.kernel.org/r/20220718235325.3952426-1-baolu.lu@linux.intel.com
Signed-off-by: Joerg Roedel <jroedel@suse.de>

authored by

Lu Baolu and committed by
Joerg Roedel
9cd4f143 91c98fe7

+12 -26
+7
drivers/iommu/intel/dmar.c
··· 2349 2349 if (!dmar_in_use()) 2350 2350 return 0; 2351 2351 2352 + /* 2353 + * It's unlikely that any I/O board is hot added before the IOMMU 2354 + * subsystem is initialized. 2355 + */ 2356 + if (IS_ENABLED(CONFIG_INTEL_IOMMU) && !intel_iommu_enabled) 2357 + return -EOPNOTSUPP; 2358 + 2352 2359 if (dmar_detect_dsm(handle, DMAR_DSM_FUNC_DRHD)) { 2353 2360 tmp = handle; 2354 2361 } else {
+2 -25
drivers/iommu/intel/iommu.c
··· 3019 3019 3020 3020 #ifdef CONFIG_INTEL_IOMMU_SVM 3021 3021 if (pasid_supported(iommu) && ecap_prs(iommu->ecap)) { 3022 - /* 3023 - * Call dmar_alloc_hwirq() with dmar_global_lock held, 3024 - * could cause possible lock race condition. 3025 - */ 3026 - up_write(&dmar_global_lock); 3027 3022 ret = intel_svm_enable_prq(iommu); 3028 - down_write(&dmar_global_lock); 3029 3023 if (ret) 3030 3024 goto free_iommu; 3031 3025 } ··· 3932 3938 force_on = (!intel_iommu_tboot_noforce && tboot_force_iommu()) || 3933 3939 platform_optin_force_iommu(); 3934 3940 3935 - down_write(&dmar_global_lock); 3936 3941 if (dmar_table_init()) { 3937 3942 if (force_on) 3938 3943 panic("tboot: Failed to initialize DMAR table\n"); ··· 3943 3950 panic("tboot: Failed to initialize DMAR device scope\n"); 3944 3951 goto out_free_dmar; 3945 3952 } 3946 - 3947 - up_write(&dmar_global_lock); 3948 - 3949 - /* 3950 - * The bus notifier takes the dmar_global_lock, so lockdep will 3951 - * complain later when we register it under the lock. 3952 - */ 3953 - dmar_register_bus_notifier(); 3954 - 3955 - down_write(&dmar_global_lock); 3956 3953 3957 3954 if (!no_iommu) 3958 3955 intel_iommu_debugfs_init(); ··· 3988 4005 pr_err("Initialization failed\n"); 3989 4006 goto out_free_dmar; 3990 4007 } 3991 - up_write(&dmar_global_lock); 3992 4008 3993 4009 init_iommu_pm_ops(); 3994 4010 3995 - down_read(&dmar_global_lock); 3996 4011 for_each_active_iommu(iommu, drhd) { 3997 4012 /* 3998 4013 * The flush queue implementation does not perform ··· 4008 4027 "%s", iommu->name); 4009 4028 iommu_device_register(&iommu->iommu, &intel_iommu_ops, NULL); 4010 4029 } 4011 - up_read(&dmar_global_lock); 4012 4030 4013 4031 bus_set_iommu(&pci_bus_type, &intel_iommu_ops); 4014 4032 if (si_domain && !hw_pass_through) 4015 4033 register_memory_notifier(&intel_iommu_memory_nb); 4016 4034 4017 - down_read(&dmar_global_lock); 4018 4035 if (probe_acpi_namespace_devices()) 4019 4036 pr_warn("ACPI name space devices didn't probe correctly\n"); 4020 4037 ··· 4023 4044 4024 4045 iommu_disable_protect_mem_regions(iommu); 4025 4046 } 4026 - up_read(&dmar_global_lock); 4027 - 4028 - pr_info("Intel(R) Virtualization Technology for Directed I/O\n"); 4029 4047 4030 4048 intel_iommu_enabled = 1; 4049 + dmar_register_bus_notifier(); 4050 + pr_info("Intel(R) Virtualization Technology for Directed I/O\n"); 4031 4051 4032 4052 return 0; 4033 4053 4034 4054 out_free_dmar: 4035 4055 intel_iommu_free_dmars(); 4036 - up_write(&dmar_global_lock); 4037 4056 return ret; 4038 4057 } 4039 4058
+3 -1
include/linux/dmar.h
··· 65 65 66 66 extern struct rw_semaphore dmar_global_lock; 67 67 extern struct list_head dmar_drhd_units; 68 + extern int intel_iommu_enabled; 68 69 69 70 #define for_each_drhd_unit(drhd) \ 70 71 list_for_each_entry_rcu(drhd, &dmar_drhd_units, list, \ ··· 89 88 static inline bool dmar_rcu_check(void) 90 89 { 91 90 return rwsem_is_locked(&dmar_global_lock) || 92 - system_state == SYSTEM_BOOTING; 91 + system_state == SYSTEM_BOOTING || 92 + (IS_ENABLED(CONFIG_INTEL_IOMMU) && !intel_iommu_enabled); 93 93 } 94 94 95 95 #define dmar_rcu_dereference(p) rcu_dereference_check((p), dmar_rcu_check())