diff options
Diffstat (limited to 'arch/x86/kernel/cpu/perf_event.c')
-rw-r--r-- | arch/x86/kernel/cpu/perf_event.c | 59 |
1 files changed, 46 insertions, 13 deletions
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index f2da20fda02d..3efdf2870a35 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c | |||
@@ -1154,7 +1154,7 @@ static int x86_pmu_handle_irq(struct pt_regs *regs) | |||
1154 | /* | 1154 | /* |
1155 | * event overflow | 1155 | * event overflow |
1156 | */ | 1156 | */ |
1157 | handled = 1; | 1157 | handled++; |
1158 | data.period = event->hw.last_period; | 1158 | data.period = event->hw.last_period; |
1159 | 1159 | ||
1160 | if (!x86_perf_event_set_period(event)) | 1160 | if (!x86_perf_event_set_period(event)) |
@@ -1200,12 +1200,20 @@ void perf_events_lapic_init(void) | |||
1200 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 1200 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
1201 | } | 1201 | } |
1202 | 1202 | ||
1203 | struct pmu_nmi_state { | ||
1204 | unsigned int marked; | ||
1205 | int handled; | ||
1206 | }; | ||
1207 | |||
1208 | static DEFINE_PER_CPU(struct pmu_nmi_state, pmu_nmi); | ||
1209 | |||
1203 | static int __kprobes | 1210 | static int __kprobes |
1204 | perf_event_nmi_handler(struct notifier_block *self, | 1211 | perf_event_nmi_handler(struct notifier_block *self, |
1205 | unsigned long cmd, void *__args) | 1212 | unsigned long cmd, void *__args) |
1206 | { | 1213 | { |
1207 | struct die_args *args = __args; | 1214 | struct die_args *args = __args; |
1208 | struct pt_regs *regs; | 1215 | unsigned int this_nmi; |
1216 | int handled; | ||
1209 | 1217 | ||
1210 | if (!atomic_read(&active_events)) | 1218 | if (!atomic_read(&active_events)) |
1211 | return NOTIFY_DONE; | 1219 | return NOTIFY_DONE; |
@@ -1214,22 +1222,47 @@ perf_event_nmi_handler(struct notifier_block *self, | |||
1214 | case DIE_NMI: | 1222 | case DIE_NMI: |
1215 | case DIE_NMI_IPI: | 1223 | case DIE_NMI_IPI: |
1216 | break; | 1224 | break; |
1217 | 1225 | case DIE_NMIUNKNOWN: | |
1226 | this_nmi = percpu_read(irq_stat.__nmi_count); | ||
1227 | if (this_nmi != __get_cpu_var(pmu_nmi).marked) | ||
1228 | /* let the kernel handle the unknown nmi */ | ||
1229 | return NOTIFY_DONE; | ||
1230 | /* | ||
1231 | * This one is a PMU back-to-back nmi. Two events | ||
1232 | * trigger 'simultaneously' raising two back-to-back | ||
1233 | * NMIs. If the first NMI handles both, the latter | ||
1234 | * will be empty and daze the CPU. So, we drop it to | ||
1235 | * avoid false-positive 'unknown nmi' messages. | ||
1236 | */ | ||
1237 | return NOTIFY_STOP; | ||
1218 | default: | 1238 | default: |
1219 | return NOTIFY_DONE; | 1239 | return NOTIFY_DONE; |
1220 | } | 1240 | } |
1221 | 1241 | ||
1222 | regs = args->regs; | ||
1223 | |||
1224 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 1242 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
1225 | /* | 1243 | |
1226 | * Can't rely on the handled return value to say it was our NMI, two | 1244 | handled = x86_pmu.handle_irq(args->regs); |
1227 | * events could trigger 'simultaneously' raising two back-to-back NMIs. | 1245 | if (!handled) |
1228 | * | 1246 | return NOTIFY_DONE; |
1229 | * If the first NMI handles both, the latter will be empty and daze | 1247 | |
1230 | * the CPU. | 1248 | this_nmi = percpu_read(irq_stat.__nmi_count); |
1231 | */ | 1249 | if ((handled > 1) || |
1232 | x86_pmu.handle_irq(regs); | 1250 | /* the next nmi could be a back-to-back nmi */ |
1251 | ((__get_cpu_var(pmu_nmi).marked == this_nmi) && | ||
1252 | (__get_cpu_var(pmu_nmi).handled > 1))) { | ||
1253 | /* | ||
1254 | * We could have two subsequent back-to-back nmis: The | ||
1255 | * first handles more than one counter, the 2nd | ||
1256 | * handles only one counter and the 3rd handles no | ||
1257 | * counter. | ||
1258 | * | ||
1259 | * This is the 2nd nmi because the previous was | ||
1260 | * handling more than one counter. We will mark the | ||
1261 | * next (3rd) and then drop it if unhandled. | ||
1262 | */ | ||
1263 | __get_cpu_var(pmu_nmi).marked = this_nmi + 1; | ||
1264 | __get_cpu_var(pmu_nmi).handled = handled; | ||
1265 | } | ||
1233 | 1266 | ||
1234 | return NOTIFY_STOP; | 1267 | return NOTIFY_STOP; |
1235 | } | 1268 | } |