Skip to content

Commit 16812c9

Browse files
Kan Liangjoergroedel
authored andcommitted
iommu/vt-d: Fix an IOMMU perfmon warning when CPU hotplug
A warning can be triggered when hotplug CPU 0. $ echo 0 > /sys/devices/system/cpu/cpu0/online ------------[ cut here ]------------ Voluntary context switch within RCU read-side critical section! WARNING: CPU: 0 PID: 19 at kernel/rcu/tree_plugin.h:318 rcu_note_context_switch+0x4f4/0x580 RIP: 0010:rcu_note_context_switch+0x4f4/0x580 Call Trace: <TASK> ? perf_event_update_userpage+0x104/0x150 __schedule+0x8d/0x960 ? perf_event_set_state.part.82+0x11/0x50 schedule+0x44/0xb0 schedule_timeout+0x226/0x310 ? __perf_event_disable+0x64/0x1a0 ? _raw_spin_unlock+0x14/0x30 wait_for_completion+0x94/0x130 __wait_rcu_gp+0x108/0x130 synchronize_rcu+0x67/0x70 ? invoke_rcu_core+0xb0/0xb0 ? __bpf_trace_rcu_stall_warning+0x10/0x10 perf_pmu_migrate_context+0x121/0x370 iommu_pmu_cpu_offline+0x6a/0xa0 ? iommu_pmu_del+0x1e0/0x1e0 cpuhp_invoke_callback+0x129/0x510 cpuhp_thread_fun+0x94/0x150 smpboot_thread_fn+0x183/0x220 ? sort_range+0x20/0x20 kthread+0xe6/0x110 ? kthread_complete_and_exit+0x20/0x20 ret_from_fork+0x1f/0x30 </TASK> ---[ end trace 0000000000000000 ]--- The synchronize_rcu() will be invoked in the perf_pmu_migrate_context(), when migrating a PMU to a new CPU. However, the current for_each_iommu() is within RCU read-side critical section. Two methods were considered to fix the issue. - Use the dmar_global_lock to replace the RCU read lock when going through the drhd list. But it triggers a lockdep warning. - Use the cpuhp_setup_state_multi() to set up a dedicated state for each IOMMU PMU. The lock can be avoided. The latter method is implemented in this patch. Since each IOMMU PMU has a dedicated state, add cpuhp_node and cpu in struct iommu_pmu to track the state. The state can be dynamically allocated now. Remove the CPUHP_AP_PERF_X86_IOMMU_PERF_ONLINE. Fixes: 46284c6 ("iommu/vt-d: Support cpumask for IOMMU perfmon") Reported-by: Ammy Yi <[email protected]> Signed-off-by: Kan Liang <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Lu Baolu <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Joerg Roedel <[email protected]>
1 parent bfd3c6b commit 16812c9

File tree

3 files changed

+46
-25
lines changed

3 files changed

+46
-25
lines changed

drivers/iommu/intel/iommu.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,8 @@ struct iommu_pmu {
641641
DECLARE_BITMAP(used_mask, IOMMU_PMU_IDX_MAX);
642642
struct perf_event *event_list[IOMMU_PMU_IDX_MAX];
643643
unsigned char irq_name[16];
644+
struct hlist_node cpuhp_node;
645+
int cpu;
644646
};
645647

646648
#define IOMMU_IRQ_ID_OFFSET_PRQ (DMAR_UNITS_SUPPORTED)

drivers/iommu/intel/perfmon.c

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -773,19 +773,34 @@ static void iommu_pmu_unset_interrupt(struct intel_iommu *iommu)
773773
iommu->perf_irq = 0;
774774
}
775775

776-
static int iommu_pmu_cpu_online(unsigned int cpu)
776+
static int iommu_pmu_cpu_online(unsigned int cpu, struct hlist_node *node)
777777
{
778+
struct iommu_pmu *iommu_pmu = hlist_entry_safe(node, typeof(*iommu_pmu), cpuhp_node);
779+
778780
if (cpumask_empty(&iommu_pmu_cpu_mask))
779781
cpumask_set_cpu(cpu, &iommu_pmu_cpu_mask);
780782

783+
if (cpumask_test_cpu(cpu, &iommu_pmu_cpu_mask))
784+
iommu_pmu->cpu = cpu;
785+
781786
return 0;
782787
}
783788

784-
static int iommu_pmu_cpu_offline(unsigned int cpu)
789+
static int iommu_pmu_cpu_offline(unsigned int cpu, struct hlist_node *node)
785790
{
786-
struct dmar_drhd_unit *drhd;
787-
struct intel_iommu *iommu;
788-
int target;
791+
struct iommu_pmu *iommu_pmu = hlist_entry_safe(node, typeof(*iommu_pmu), cpuhp_node);
792+
int target = cpumask_first(&iommu_pmu_cpu_mask);
793+
794+
/*
795+
* The iommu_pmu_cpu_mask has been updated when offline the CPU
796+
* for the first iommu_pmu. Migrate the other iommu_pmu to the
797+
* new target.
798+
*/
799+
if (target < nr_cpu_ids && target != iommu_pmu->cpu) {
800+
perf_pmu_migrate_context(&iommu_pmu->pmu, cpu, target);
801+
iommu_pmu->cpu = target;
802+
return 0;
803+
}
789804

790805
if (!cpumask_test_and_clear_cpu(cpu, &iommu_pmu_cpu_mask))
791806
return 0;
@@ -795,45 +810,50 @@ static int iommu_pmu_cpu_offline(unsigned int cpu)
795810
if (target < nr_cpu_ids)
796811
cpumask_set_cpu(target, &iommu_pmu_cpu_mask);
797812
else
798-
target = -1;
813+
return 0;
799814

800-
rcu_read_lock();
801-
802-
for_each_iommu(iommu, drhd) {
803-
if (!iommu->pmu)
804-
continue;
805-
perf_pmu_migrate_context(&iommu->pmu->pmu, cpu, target);
806-
}
807-
rcu_read_unlock();
815+
perf_pmu_migrate_context(&iommu_pmu->pmu, cpu, target);
816+
iommu_pmu->cpu = target;
808817

809818
return 0;
810819
}
811820

812821
static int nr_iommu_pmu;
822+
static enum cpuhp_state iommu_cpuhp_slot;
813823

814824
static int iommu_pmu_cpuhp_setup(struct iommu_pmu *iommu_pmu)
815825
{
816826
int ret;
817827

818-
if (nr_iommu_pmu++)
819-
return 0;
828+
if (!nr_iommu_pmu) {
829+
ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
830+
"driver/iommu/intel/perfmon:online",
831+
iommu_pmu_cpu_online,
832+
iommu_pmu_cpu_offline);
833+
if (ret < 0)
834+
return ret;
835+
iommu_cpuhp_slot = ret;
836+
}
820837

821-
ret = cpuhp_setup_state(CPUHP_AP_PERF_X86_IOMMU_PERF_ONLINE,
822-
"driver/iommu/intel/perfmon:online",
823-
iommu_pmu_cpu_online,
824-
iommu_pmu_cpu_offline);
825-
if (ret)
826-
nr_iommu_pmu = 0;
838+
ret = cpuhp_state_add_instance(iommu_cpuhp_slot, &iommu_pmu->cpuhp_node);
839+
if (ret) {
840+
if (!nr_iommu_pmu)
841+
cpuhp_remove_multi_state(iommu_cpuhp_slot);
842+
return ret;
843+
}
844+
nr_iommu_pmu++;
827845

828-
return ret;
846+
return 0;
829847
}
830848

831849
static void iommu_pmu_cpuhp_free(struct iommu_pmu *iommu_pmu)
832850
{
851+
cpuhp_state_remove_instance(iommu_cpuhp_slot, &iommu_pmu->cpuhp_node);
852+
833853
if (--nr_iommu_pmu)
834854
return;
835855

836-
cpuhp_remove_state(CPUHP_AP_PERF_X86_IOMMU_PERF_ONLINE);
856+
cpuhp_remove_multi_state(iommu_cpuhp_slot);
837857
}
838858

839859
void iommu_pmu_register(struct intel_iommu *iommu)

include/linux/cpuhotplug.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,6 @@ enum cpuhp_state {
218218
CPUHP_AP_PERF_X86_CQM_ONLINE,
219219
CPUHP_AP_PERF_X86_CSTATE_ONLINE,
220220
CPUHP_AP_PERF_X86_IDXD_ONLINE,
221-
CPUHP_AP_PERF_X86_IOMMU_PERF_ONLINE,
222221
CPUHP_AP_PERF_S390_CF_ONLINE,
223222
CPUHP_AP_PERF_S390_SF_ONLINE,
224223
CPUHP_AP_PERF_ARM_CCI_ONLINE,

0 commit comments

Comments
 (0)